@stigg/react-sdk 4.13.2 → 4.15.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/common/TiersSelectContainer.d.ts +8 -3
- package/dist/components/common/VolumeBulkSelect.d.ts +5 -0
- package/dist/components/common/VolumePerUnitInput.d.ts +5 -0
- package/dist/components/customerPortal/CustomerPortal.d.ts +2 -2
- package/dist/components/customerPortal/CustomerPortalContainer.d.ts +4 -0
- package/dist/components/customerPortal/index.d.ts +3 -0
- package/dist/components/customerPortal/subscriptionOverview/SubscriptionsOverview.d.ts +2 -1
- package/dist/components/customerPortal/subscriptionOverview/SubscriptionsOverviewHeader.d.ts +2 -1
- package/dist/components/customerPortal/subscriptionOverview/charges/ChargeItem.d.ts +2 -1
- package/dist/components/customerPortal/subscriptionOverview/charges/ChargeList.d.ts +2 -1
- package/dist/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.d.ts +2 -1
- package/dist/components/customerPortal/subscriptionOverview/subscriptionView/TrialPanel.d.ts +2 -1
- package/dist/components/customerPortal/types.d.ts +10 -1
- package/dist/components/customerPortal/usage/CustomerUsageData.d.ts +2 -1
- package/dist/components/customerPortal/usage/featureUsage/EntitlementCTAButton.d.ts +2 -1
- package/dist/components/customerPortal/usage/featureUsage/FeatureUsage.d.ts +2 -1
- package/dist/components/paywall/PlanOfferingButton.d.ts +2 -1
- package/dist/components/paywall/PlanPrice.d.ts +3 -1
- package/dist/components/paywall/index.d.ts +1 -1
- package/dist/components/paywall/paywallTextOverrides.d.ts +1 -1
- package/dist/components/utils/getPaidPriceText.d.ts +2 -1
- package/dist/components/utils/getPlanPrice.d.ts +1 -1
- package/dist/components/utils/priceTierUtils.d.ts +3 -2
- package/dist/index.d.ts +3 -3
- package/dist/react-sdk.cjs.development.js +555 -361
- 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 +589 -388
- package/dist/react-sdk.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/checkout/hooks/usePlanStepModel.ts +3 -3
- package/src/components/checkout/steps/plan/CheckoutChargeList.tsx +10 -2
- package/src/components/common/TiersSelectContainer.tsx +33 -62
- package/src/components/common/VolumeBulkSelect.tsx +68 -0
- package/src/components/common/VolumePerUnitInput.tsx +40 -0
- package/src/components/customerPortal/CustomerPortal.tsx +2 -2
- package/src/components/customerPortal/CustomerPortalContainer.tsx +8 -3
- package/src/components/customerPortal/index.ts +3 -0
- package/src/components/customerPortal/subscriptionOverview/SubscriptionsOverview.tsx +2 -1
- package/src/components/customerPortal/subscriptionOverview/SubscriptionsOverviewHeader.tsx +7 -2
- package/src/components/customerPortal/subscriptionOverview/charges/ChargeItem.tsx +2 -1
- package/src/components/customerPortal/subscriptionOverview/charges/ChargeList.tsx +2 -1
- package/src/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.tsx +2 -1
- package/src/components/customerPortal/subscriptionOverview/subscriptionView/TrialPanel.tsx +6 -2
- package/src/components/customerPortal/types.ts +15 -1
- package/src/components/customerPortal/usage/CustomerUsageData.tsx +2 -1
- package/src/components/customerPortal/usage/featureUsage/EntitlementCTAButton.tsx +6 -3
- package/src/components/customerPortal/usage/featureUsage/FeatureUsage.tsx +2 -1
- package/src/components/paywall/PlanOffering.tsx +15 -6
- package/src/components/paywall/PlanOfferingButton.tsx +7 -1
- package/src/components/paywall/PlanPrice.tsx +20 -0
- package/src/components/paywall/index.ts +1 -1
- package/src/components/utils/getPaidPriceText.ts +9 -1
- package/src/components/utils/getPlanPrice.ts +2 -0
- package/src/components/utils/priceTierUtils.ts +60 -14
- package/src/index.ts +9 -1
- package/src/stories/mocks/checkout/mockCheckoutPreview.ts +1 -1
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.15.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"@emotion/react": "^11.10.5",
|
|
113
113
|
"@emotion/styled": "^11.10.5",
|
|
114
114
|
"@mui/material": "^5.12.0",
|
|
115
|
-
"@stigg/js-client-sdk": "2.
|
|
115
|
+
"@stigg/js-client-sdk": "2.25.0",
|
|
116
116
|
"@stripe/react-stripe-js": "^2.1.1",
|
|
117
117
|
"@stripe/stripe-js": "^1.54.1",
|
|
118
118
|
"@types/styled-components": "^5.1.26",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import keyBy from 'lodash/keyBy';
|
|
2
2
|
import compact from 'lodash/compact';
|
|
3
|
-
import { BillableFeatureInput, BillingPeriod } from '@stigg/api-client-js/src/generated/sdk';
|
|
3
|
+
import { BillableFeatureInput, BillingPeriod, TiersMode } from '@stigg/api-client-js/src/generated/sdk';
|
|
4
4
|
import { BillableFeature, BillingModel, Plan, Price, Subscription } from '@stigg/js-client-sdk';
|
|
5
5
|
import { useCheckoutContext } from '../CheckoutProvider';
|
|
6
6
|
import { getTierByQuantity } from '../../utils/priceTierUtils';
|
|
@@ -73,9 +73,9 @@ function getBillableFeatures(
|
|
|
73
73
|
|
|
74
74
|
quantity = getValidPriceQuantity(price, preconfiguredQuantity || 1);
|
|
75
75
|
|
|
76
|
-
if (price.isTieredPrice) {
|
|
76
|
+
if (price.isTieredPrice && price.tiersMode === TiersMode.Volume) {
|
|
77
77
|
const tier = getTierByQuantity(price.tiers!, quantity);
|
|
78
|
-
quantity = tier!.upTo
|
|
78
|
+
quantity = tier!.upTo!;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
return {
|
|
@@ -103,7 +103,7 @@ export function PlanCharge({
|
|
|
103
103
|
if (isPayAsYouGo) {
|
|
104
104
|
chargeRow += ' / unit';
|
|
105
105
|
}
|
|
106
|
-
} else if (charge.isTieredPrice && featureId) {
|
|
106
|
+
} else if (charge.isTieredPrice && charge.tiersMode && featureId) {
|
|
107
107
|
const tier = getTierByQuantity(charge.tiers!, billableFeature!.quantity || 1);
|
|
108
108
|
chargeRow = (
|
|
109
109
|
<TiersSelectContainer
|
|
@@ -111,8 +111,16 @@ export function PlanCharge({
|
|
|
111
111
|
tiers={charge.tiers}
|
|
112
112
|
selectedTier={tier}
|
|
113
113
|
handleTierChange={(tier: PriceTierFragment) => {
|
|
114
|
-
|
|
114
|
+
if (tier.upTo) {
|
|
115
|
+
setBillableFeature(featureId, tier.upTo);
|
|
116
|
+
}
|
|
115
117
|
}}
|
|
118
|
+
tiersMode={charge.tiersMode}
|
|
119
|
+
perUnitQuantity={billableFeature?.quantity}
|
|
120
|
+
handlePerUnitQuantityChange={(quantity: number) => {
|
|
121
|
+
setBillableFeature(featureId, quantity);
|
|
122
|
+
}}
|
|
123
|
+
width={120}
|
|
116
124
|
/>
|
|
117
125
|
);
|
|
118
126
|
} else {
|
|
@@ -1,78 +1,49 @@
|
|
|
1
|
-
import { PriceTierFragment } from '@stigg/js-client-sdk';
|
|
2
|
-
import
|
|
3
|
-
import OutlinedInput from '@mui/material/OutlinedInput';
|
|
4
|
-
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
|
5
|
-
import { map } from 'lodash';
|
|
6
|
-
import React, { ReactNode } from 'react';
|
|
1
|
+
import { PriceTierFragment, TiersMode } from '@stigg/js-client-sdk';
|
|
2
|
+
import React from 'react';
|
|
7
3
|
import styled from '@emotion/styled/macro';
|
|
8
4
|
import { getTierByQuantity } from '../utils/priceTierUtils';
|
|
9
5
|
import { Typography } from './Typography';
|
|
6
|
+
import { VolumePerUnitInput } from './VolumePerUnitInput';
|
|
7
|
+
import { VolumeBulkSelect } from './VolumeBulkSelect';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
border-radius: 10px;
|
|
13
|
-
min-height: 42px;
|
|
14
|
-
min-width: 120px;
|
|
15
|
-
|
|
16
|
-
&:hover .MuiOutlinedInput-notchedOutline {
|
|
17
|
-
border-color: ${({ theme }) => theme.stigg.palette.outlinedBorder};
|
|
18
|
-
}
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
const TierInput = styled(OutlinedInput)`
|
|
22
|
-
& .MuiInputBase-input {
|
|
23
|
-
padding: 10px 12px;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
&.Mui-focused .MuiOutlinedInput-notchedOutline {
|
|
27
|
-
border-color: ${({ theme }) => theme.stigg.palette.primary} !important;
|
|
28
|
-
}
|
|
29
|
-
`;
|
|
30
|
-
|
|
31
|
-
export function TiersSelectContainer({
|
|
32
|
-
componentId,
|
|
33
|
-
tiers,
|
|
34
|
-
tierUnits,
|
|
35
|
-
selectedTier,
|
|
36
|
-
handleTierChange,
|
|
37
|
-
}: {
|
|
9
|
+
export type TiersSelectContainerProps = {
|
|
38
10
|
componentId: string;
|
|
39
11
|
tiers?: PriceTierFragment[] | null;
|
|
40
12
|
tierUnits?: string;
|
|
41
13
|
selectedTier?: PriceTierFragment;
|
|
42
14
|
handleTierChange: (tier: PriceTierFragment) => void;
|
|
43
|
-
|
|
44
|
-
|
|
15
|
+
tiersMode?: TiersMode | null;
|
|
16
|
+
handlePerUnitQuantityChange: (quantity: number) => void;
|
|
17
|
+
perUnitQuantity?: number;
|
|
18
|
+
width?: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const TiersSelectLayout = styled(Typography)`
|
|
22
|
+
min-height: 46px;
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
export function TiersSelectContainer(props: TiersSelectContainerProps) {
|
|
26
|
+
const { tiers, tiersMode, handleTierChange } = props;
|
|
27
|
+
const handleChange = (quantity: number) => {
|
|
45
28
|
if (!tiers) return;
|
|
46
29
|
|
|
47
|
-
handleTierChange(getTierByQuantity(tiers,
|
|
30
|
+
handleTierChange(getTierByQuantity(tiers, quantity)!);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getContent = () => {
|
|
34
|
+
switch (tiersMode) {
|
|
35
|
+
case TiersMode.VolumePerUnit:
|
|
36
|
+
return <VolumePerUnitInput handleChange={handleChange} {...props} />;
|
|
37
|
+
case TiersMode.Volume:
|
|
38
|
+
return <VolumeBulkSelect handleChange={handleChange} {...props} />;
|
|
39
|
+
default:
|
|
40
|
+
return <div />;
|
|
41
|
+
}
|
|
48
42
|
};
|
|
49
43
|
|
|
50
44
|
return (
|
|
51
|
-
<
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
value={selectedTier ? selectedTier.upTo.toString() : tiers[0].upTo.toString()}
|
|
55
|
-
fullWidth
|
|
56
|
-
onChange={handleChange}
|
|
57
|
-
id={componentId}
|
|
58
|
-
input={<TierInput />}
|
|
59
|
-
MenuProps={{
|
|
60
|
-
MenuListProps: { disablePadding: true },
|
|
61
|
-
PaperProps: {
|
|
62
|
-
sx: { marginTop: '4px', borderRadius: '10px' },
|
|
63
|
-
},
|
|
64
|
-
}}>
|
|
65
|
-
{map(tiers, (tier: PriceTierFragment) => (
|
|
66
|
-
<MenuItem className="stigg-price-tier-menu-item-text" key={tier.upTo} value={tier.upTo.toString()}>
|
|
67
|
-
<Typography variant="body1" color="primary" style={{ lineHeight: 'unset' }}>
|
|
68
|
-
{tier.upTo} {tierUnits}
|
|
69
|
-
</Typography>
|
|
70
|
-
</MenuItem>
|
|
71
|
-
))}
|
|
72
|
-
</TierSelect>
|
|
73
|
-
) : (
|
|
74
|
-
<div />
|
|
75
|
-
)}
|
|
76
|
-
</Typography>
|
|
45
|
+
<TiersSelectLayout as="div" className="stigg-price-tier-select">
|
|
46
|
+
{getContent()}
|
|
47
|
+
</TiersSelectLayout>
|
|
77
48
|
);
|
|
78
49
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { PriceTierFragment } from '@stigg/js-client-sdk';
|
|
2
|
+
import styled from '@emotion/styled/macro';
|
|
3
|
+
import OutlinedInput from '@mui/material/OutlinedInput';
|
|
4
|
+
import Select from '@mui/material/Select';
|
|
5
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
6
|
+
import { map } from 'lodash';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { Typography } from './Typography';
|
|
9
|
+
import { TiersSelectContainerProps } from './TiersSelectContainer';
|
|
10
|
+
|
|
11
|
+
const TierSelect = styled(Select)`
|
|
12
|
+
border-radius: 10px;
|
|
13
|
+
min-height: 42px;
|
|
14
|
+
min-width: 120px;
|
|
15
|
+
|
|
16
|
+
&:hover .MuiOutlinedInput-notchedOutline {
|
|
17
|
+
border-color: ${({ theme }) => theme.stigg.palette.outlinedBorder};
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const TierInput = styled(OutlinedInput)`
|
|
22
|
+
& .MuiInputBase-input {
|
|
23
|
+
padding: 10px 12px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&.Mui-focused .MuiOutlinedInput-notchedOutline {
|
|
27
|
+
border-color: ${({ theme }) => theme.stigg.palette.primary} !important;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
export function VolumeBulkSelect({
|
|
32
|
+
selectedTier,
|
|
33
|
+
componentId,
|
|
34
|
+
tierUnits,
|
|
35
|
+
tiers,
|
|
36
|
+
handleChange,
|
|
37
|
+
}: Pick<TiersSelectContainerProps, 'selectedTier' | 'componentId' | 'tiers' | 'tierUnits'> & {
|
|
38
|
+
handleChange: (quantity: number) => void;
|
|
39
|
+
}) {
|
|
40
|
+
if (!tiers) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<TierSelect
|
|
46
|
+
value={selectedTier ? selectedTier.upTo?.toString() : tiers[0].upTo?.toString()}
|
|
47
|
+
fullWidth
|
|
48
|
+
onChange={(event) => {
|
|
49
|
+
handleChange(event.target.value as number);
|
|
50
|
+
}}
|
|
51
|
+
id={componentId}
|
|
52
|
+
input={<TierInput />}
|
|
53
|
+
MenuProps={{
|
|
54
|
+
MenuListProps: { disablePadding: true },
|
|
55
|
+
PaperProps: {
|
|
56
|
+
sx: { marginTop: '4px', borderRadius: '10px' },
|
|
57
|
+
},
|
|
58
|
+
}}>
|
|
59
|
+
{map(tiers, (tier: PriceTierFragment) => (
|
|
60
|
+
<MenuItem className="stigg-price-tier-menu-item-text" key={tier.upTo} value={tier.upTo?.toString()}>
|
|
61
|
+
<Typography variant="body1" color="primary" style={{ lineHeight: 'unset' }}>
|
|
62
|
+
{tier.upTo} {tierUnits}
|
|
63
|
+
</Typography>
|
|
64
|
+
</MenuItem>
|
|
65
|
+
))}
|
|
66
|
+
</TierSelect>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Typography } from './Typography';
|
|
4
|
+
import { InputField } from '../checkout/components';
|
|
5
|
+
import { TiersSelectContainerProps } from './TiersSelectContainer';
|
|
6
|
+
|
|
7
|
+
export function VolumePerUnitInput({
|
|
8
|
+
width,
|
|
9
|
+
tierUnits,
|
|
10
|
+
perUnitQuantity,
|
|
11
|
+
handlePerUnitQuantityChange,
|
|
12
|
+
handleChange,
|
|
13
|
+
}: Pick<TiersSelectContainerProps, 'width' | 'tierUnits' | 'perUnitQuantity' | 'handlePerUnitQuantityChange'> & {
|
|
14
|
+
handleChange: (quantity: number) => void;
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<InputField
|
|
18
|
+
type="number"
|
|
19
|
+
fullWidth
|
|
20
|
+
sx={{ minHeight: '46px', width }}
|
|
21
|
+
InputProps={{
|
|
22
|
+
endAdornment: (
|
|
23
|
+
<InputAdornment position="end">
|
|
24
|
+
<Typography variant="body1" color="primary">
|
|
25
|
+
{tierUnits}
|
|
26
|
+
</Typography>
|
|
27
|
+
</InputAdornment>
|
|
28
|
+
),
|
|
29
|
+
}}
|
|
30
|
+
value={perUnitQuantity || 1}
|
|
31
|
+
onChange={(event) => {
|
|
32
|
+
const quantity = event?.target?.value ? Number(event?.target?.value) : null;
|
|
33
|
+
if (quantity) {
|
|
34
|
+
handleChange(quantity);
|
|
35
|
+
handlePerUnitQuantityChange(quantity);
|
|
36
|
+
}
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -5,7 +5,7 @@ import { CustomerPortalContainer } from './CustomerPortalContainer';
|
|
|
5
5
|
import { DeepPartial } from '../../types';
|
|
6
6
|
import { CustomerPortalLocalization } from './customerPortalTextOverrides';
|
|
7
7
|
import { CustomerPortalTheme } from './customerPortalTheme';
|
|
8
|
-
import { OnBuyMoreCallbackFn } from './types';
|
|
8
|
+
import { OnBuyMoreCallbackFn, OnManageSubscriptionFn } from './types';
|
|
9
9
|
|
|
10
10
|
export type CustomerPortalSection =
|
|
11
11
|
| 'usage'
|
|
@@ -16,7 +16,7 @@ export type CustomerPortalSection =
|
|
|
16
16
|
| 'invoices';
|
|
17
17
|
|
|
18
18
|
export type CustomerPortalProps = {
|
|
19
|
-
onManageSubscription?:
|
|
19
|
+
onManageSubscription?: OnManageSubscriptionFn;
|
|
20
20
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
21
21
|
onCancelScheduledUpdates?: (subscription: CustomerPortalSubscription) => Promise<void> | void;
|
|
22
22
|
onContactSupport?: () => Promise<void> | void;
|
|
@@ -8,6 +8,9 @@ import { CustomerPortalLayout, CustomerPortalSections } from './CustomerPortal.s
|
|
|
8
8
|
import { CustomerPortalProps } from './CustomerPortal';
|
|
9
9
|
import { InvoicesSection, useStiggContext } from '../..';
|
|
10
10
|
import { CustomerUsageData } from './usage/CustomerUsageData';
|
|
11
|
+
import { CustomerPortalIntentionType } from './types';
|
|
12
|
+
|
|
13
|
+
export type OnManageClick = ({ intentionType }: { intentionType: CustomerPortalIntentionType }) => void;
|
|
11
14
|
|
|
12
15
|
export function CustomerPortalContainer({
|
|
13
16
|
onManageSubscription,
|
|
@@ -21,10 +24,12 @@ export function CustomerPortalContainer({
|
|
|
21
24
|
const { customerPortal, textOverrides, theme, isLoading } = useCustomerPortalContext();
|
|
22
25
|
const customerPortalSectionRef = useRef<HTMLDivElement>(null);
|
|
23
26
|
|
|
24
|
-
const onManageClick = () => {
|
|
27
|
+
const onManageClick: OnManageClick = ({ intentionType }: { intentionType: CustomerPortalIntentionType }) => {
|
|
25
28
|
if (onManageSubscription) {
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
void onManageSubscription({
|
|
30
|
+
intentionType,
|
|
31
|
+
customerSubscriptions: customerPortal?.subscriptions ?? [],
|
|
32
|
+
});
|
|
28
33
|
} else {
|
|
29
34
|
customerPortalSectionRef?.current?.scrollIntoView({ behavior: 'smooth' });
|
|
30
35
|
}
|
|
@@ -6,3 +6,6 @@ export { CustomerUsageData, CustomerUsageDataProps } from './usage/CustomerUsage
|
|
|
6
6
|
export { AddonsList } from './subscriptionOverview/tabs/AddonsList';
|
|
7
7
|
export { Promotions } from './subscriptionOverview/tabs/Promotions';
|
|
8
8
|
export { CustomerPortalProvider } from './CustomerPortalProvider';
|
|
9
|
+
export { CustomerPortalLocalization } from './customerPortalTextOverrides';
|
|
10
|
+
export { CustomerPortalTheme } from './customerPortalTheme';
|
|
11
|
+
export * from './types';
|
|
@@ -11,9 +11,10 @@ import { UpcomingBilling } from './upcomingBilling/UpcomingBilling';
|
|
|
11
11
|
import { SubscriptionView } from './subscriptionView/SubscriptionView';
|
|
12
12
|
import { ChargeList } from './charges/ChargeList';
|
|
13
13
|
import { OnBuyMoreCallbackFn } from '../types';
|
|
14
|
+
import { OnManageClick } from '../CustomerPortalContainer';
|
|
14
15
|
|
|
15
16
|
export type SubscriptionsOverviewProps = {
|
|
16
|
-
onManageSubscription:
|
|
17
|
+
onManageSubscription: OnManageClick;
|
|
17
18
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
18
19
|
onCancelScheduledUpdates?: (subscription: CustomerPortalSubscription) => void;
|
|
19
20
|
onContactSupport?: () => void;
|
|
@@ -6,9 +6,11 @@ import EditIcon from '../../../assets/edit-icon.svg';
|
|
|
6
6
|
import { CustomerPortalLocalization } from '../customerPortalTextOverrides';
|
|
7
7
|
import { SectionHeader } from '../common/SectionHeader';
|
|
8
8
|
import { SectionTitle } from '../common/SectionTitle';
|
|
9
|
+
import { OnManageClick } from '../CustomerPortalContainer';
|
|
10
|
+
import { CustomerPortalIntentionType } from '../types';
|
|
9
11
|
|
|
10
12
|
type SubscriptionsOverviewHeaderProps = {
|
|
11
|
-
onManageSubscription:
|
|
13
|
+
onManageSubscription: OnManageClick;
|
|
12
14
|
hideManageButton: boolean;
|
|
13
15
|
textOverrides: CustomerPortalLocalization;
|
|
14
16
|
};
|
|
@@ -28,7 +30,10 @@ export function SubscriptionsOverviewHeader({
|
|
|
28
30
|
<SectionHeader className="stigg-customer-portal-overview-header" $disableMargin>
|
|
29
31
|
<SectionTitle title="Subscription" className="stigg-overview-title" />
|
|
30
32
|
{!hideManageButton && (
|
|
31
|
-
<StyledButton
|
|
33
|
+
<StyledButton
|
|
34
|
+
className="stigg-manage-subscription-button"
|
|
35
|
+
variant="outlined"
|
|
36
|
+
onClick={() => onManageSubscription({ intentionType: CustomerPortalIntentionType.MANAGE_SUBSCRIPTION })}>
|
|
32
37
|
<StyledEditIcon className="stigg-manage-subscription-button-image" />
|
|
33
38
|
<Typography
|
|
34
39
|
className="stigg-manage-subscription-button-text"
|
|
@@ -7,6 +7,7 @@ import { FeatureUsageProgressBar } from '../../usage/featureUsage/FeatureUsagePr
|
|
|
7
7
|
import { LongText } from '../../../common/LongText';
|
|
8
8
|
import { EntitlementCtaButton } from '../../usage/featureUsage/EntitlementCTAButton';
|
|
9
9
|
import { OnBuyMoreCallbackFn } from '../../types';
|
|
10
|
+
import { OnManageClick } from '../../CustomerPortalContainer';
|
|
10
11
|
|
|
11
12
|
export type UsageBasedChargeProps = {
|
|
12
13
|
entitlement: Exclude<CustomerPortalEntitlement, 'feature'>;
|
|
@@ -14,7 +15,7 @@ export type UsageBasedChargeProps = {
|
|
|
14
15
|
amount: number;
|
|
15
16
|
currency: Currency;
|
|
16
17
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
17
|
-
onManageSubscription:
|
|
18
|
+
onManageSubscription: OnManageClick;
|
|
18
19
|
canUpgradeSubscription: boolean;
|
|
19
20
|
hasCustomSubscription: boolean;
|
|
20
21
|
hideChargePrice?: boolean;
|
|
@@ -3,12 +3,13 @@ import React from 'react';
|
|
|
3
3
|
import { keyBy } from 'lodash';
|
|
4
4
|
import { ChargeItem } from './ChargeItem';
|
|
5
5
|
import { OnBuyMoreCallbackFn } from '../../types';
|
|
6
|
+
import { OnManageClick } from '../../CustomerPortalContainer';
|
|
6
7
|
|
|
7
8
|
export type ChargesProps = {
|
|
8
9
|
subscription: CustomerPortalSubscription;
|
|
9
10
|
entitlements: CustomerPortalEntitlement[];
|
|
10
11
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
11
|
-
onManageSubscription:
|
|
12
|
+
onManageSubscription: OnManageClick;
|
|
12
13
|
canUpgradeSubscription: boolean;
|
|
13
14
|
hasCustomSubscription: boolean;
|
|
14
15
|
};
|
package/src/components/customerPortal/subscriptionOverview/subscriptionView/SubscriptionView.tsx
CHANGED
|
@@ -3,11 +3,12 @@ import { CustomerPortalSubscription } from '@stigg/js-client-sdk';
|
|
|
3
3
|
import { Typography } from '../../../common/Typography';
|
|
4
4
|
import { TrialPanel } from './TrialPanel';
|
|
5
5
|
import { CustomerPortalTheme } from '../../customerPortalTheme';
|
|
6
|
+
import { OnManageClick } from '../../CustomerPortalContainer';
|
|
6
7
|
|
|
7
8
|
type SubscriptionViewProps = {
|
|
8
9
|
mainSubscription: CustomerPortalSubscription;
|
|
9
10
|
trialSubscription: CustomerPortalSubscription | undefined;
|
|
10
|
-
onManageSubscription:
|
|
11
|
+
onManageSubscription: OnManageClick;
|
|
11
12
|
theme: CustomerPortalTheme;
|
|
12
13
|
};
|
|
13
14
|
|
|
@@ -5,6 +5,8 @@ import Link from '@mui/material/Link';
|
|
|
5
5
|
import SandClockIcon from '../../../../assets/sand-clock.svg';
|
|
6
6
|
import { Typography } from '../../../common/Typography';
|
|
7
7
|
import { LongText } from '../../../common/LongText';
|
|
8
|
+
import { OnManageClick } from '../../CustomerPortalContainer';
|
|
9
|
+
import { CustomerPortalIntentionType } from '../../types';
|
|
8
10
|
|
|
9
11
|
const OnTrialBadge = styled.div`
|
|
10
12
|
width: 100%;
|
|
@@ -30,7 +32,7 @@ export function TrialPanel({
|
|
|
30
32
|
}: {
|
|
31
33
|
subscription: CustomerPortalSubscription | undefined;
|
|
32
34
|
includePlanName: boolean;
|
|
33
|
-
onManageSubscription
|
|
35
|
+
onManageSubscription: OnManageClick;
|
|
34
36
|
}) {
|
|
35
37
|
if (subscription?.status !== SubscriptionStatus.InTrial) {
|
|
36
38
|
return null;
|
|
@@ -47,7 +49,9 @@ export function TrialPanel({
|
|
|
47
49
|
</LongText>
|
|
48
50
|
</div>
|
|
49
51
|
|
|
50
|
-
<StyledLink
|
|
52
|
+
<StyledLink
|
|
53
|
+
className="stigg-subscription-trial-upgrade-button"
|
|
54
|
+
onClick={() => onManageSubscription({ intentionType: CustomerPortalIntentionType.UPGRADE_TRIAL_PLAN })}>
|
|
51
55
|
<Typography color="white">Upgrade plan</Typography>
|
|
52
56
|
</StyledLink>
|
|
53
57
|
</OnTrialBadge>
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
import { CustomerPortalEntitlement } from '@stigg/js-client-sdk';
|
|
1
|
+
import { CustomerPortalEntitlement, CustomerPortalSubscription } from '@stigg/js-client-sdk';
|
|
2
2
|
import { FeatureFragment } from '@stigg/api-client-js/src/generated/sdk';
|
|
3
3
|
|
|
4
4
|
export type OnBuyMoreCallbackFn = (feature: FeatureFragment, entitlement: CustomerPortalEntitlement) => void;
|
|
5
|
+
|
|
6
|
+
export enum CustomerPortalIntentionType {
|
|
7
|
+
MANAGE_SUBSCRIPTION = 'MANAGE_SUBSCRIPTION',
|
|
8
|
+
UPGRADE_PLAN = 'UPGRADE_PLAN',
|
|
9
|
+
UPGRADE_TRIAL_PLAN = 'UPGRADE_TRIAL_PLAN',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type OnManageSubscriptionFn = ({
|
|
13
|
+
intentionType,
|
|
14
|
+
customerSubscriptions,
|
|
15
|
+
}: {
|
|
16
|
+
intentionType: CustomerPortalIntentionType;
|
|
17
|
+
customerSubscriptions: CustomerPortalSubscription[];
|
|
18
|
+
}) => Promise<void> | void;
|
|
@@ -11,11 +11,12 @@ import { SectionHeader } from '../common/SectionHeader';
|
|
|
11
11
|
import { SectionTitle } from '../common/SectionTitle';
|
|
12
12
|
import { StyledButton } from '../common/StyledButton';
|
|
13
13
|
import { OnBuyMoreCallbackFn } from '../types';
|
|
14
|
+
import { OnManageClick } from '../CustomerPortalContainer';
|
|
14
15
|
|
|
15
16
|
const MAX_BOXES = 6;
|
|
16
17
|
|
|
17
18
|
export type CustomerUsageDataProps = {
|
|
18
|
-
onManageSubscription?:
|
|
19
|
+
onManageSubscription?: OnManageClick;
|
|
19
20
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
20
21
|
};
|
|
21
22
|
|
|
@@ -5,7 +5,8 @@ import React from 'react';
|
|
|
5
5
|
import styled from 'styled-components';
|
|
6
6
|
import { getUsagePercentage, USAGE_PERCENTAGE_WARNING_THRESHOLD } from './FeatureUsage.helper';
|
|
7
7
|
import { Typography } from '../../../common/Typography';
|
|
8
|
-
import { OnBuyMoreCallbackFn } from '../../types';
|
|
8
|
+
import { CustomerPortalIntentionType, OnBuyMoreCallbackFn } from '../../types';
|
|
9
|
+
import { OnManageClick } from '../../CustomerPortalContainer';
|
|
9
10
|
|
|
10
11
|
const StyledButton = styled(Button)`
|
|
11
12
|
text-transform: none;
|
|
@@ -16,7 +17,7 @@ const StyledButton = styled(Button)`
|
|
|
16
17
|
export type EntitlementCtaButtonProps = {
|
|
17
18
|
entitlement: Exclude<CustomerPortalEntitlement, 'feature'>;
|
|
18
19
|
feature: FeatureFragment;
|
|
19
|
-
onManageSubscription:
|
|
20
|
+
onManageSubscription: OnManageClick | undefined;
|
|
20
21
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
21
22
|
canBuyMore: boolean;
|
|
22
23
|
canUpgradeSubscription: boolean;
|
|
@@ -50,7 +51,9 @@ export function EntitlementCtaButton({
|
|
|
50
51
|
|
|
51
52
|
if (canUpgradeSubscription) {
|
|
52
53
|
return onManageSubscription ? (
|
|
53
|
-
<StyledButton
|
|
54
|
+
<StyledButton
|
|
55
|
+
variant="text"
|
|
56
|
+
onClick={() => onManageSubscription({ intentionType: CustomerPortalIntentionType.UPGRADE_PLAN })}>
|
|
54
57
|
<Typography color="primary.main">Upgrade</Typography>
|
|
55
58
|
</StyledButton>
|
|
56
59
|
) : null;
|
|
@@ -13,11 +13,12 @@ import { InformationTooltip } from '../../../common/InformationTooltip';
|
|
|
13
13
|
import { LongText } from '../../../common/LongText';
|
|
14
14
|
import { EntitlementCtaButton } from './EntitlementCTAButton';
|
|
15
15
|
import { OnBuyMoreCallbackFn } from '../../types';
|
|
16
|
+
import { OnManageClick } from '../../CustomerPortalContainer';
|
|
16
17
|
|
|
17
18
|
export type UsageBoxProps = {
|
|
18
19
|
entitlement: CustomerPortalEntitlement;
|
|
19
20
|
subscriptionPrice: CustomerPortalSubscriptionPriceFragment | undefined;
|
|
20
|
-
onManageSubscription:
|
|
21
|
+
onManageSubscription: OnManageClick | undefined;
|
|
21
22
|
onBuyMore?: OnBuyMoreCallbackFn;
|
|
22
23
|
hasCustomSubscription: boolean;
|
|
23
24
|
canUpgradeSubscription: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
BillableFeature,
|
|
4
4
|
BillingPeriod,
|
|
@@ -18,7 +18,7 @@ import { flexLayoutMapper } from '../../theme/getResolvedTheme';
|
|
|
18
18
|
import { Typography } from '../common/Typography';
|
|
19
19
|
import MiniSchedule from '../../assets/mini-schedule.svg';
|
|
20
20
|
import { PlanPrice } from './PlanPrice';
|
|
21
|
-
import { getSelectedTier } from '../utils/priceTierUtils';
|
|
21
|
+
import { getSelectedTier, getSelectedTierQuantityBeFeature } from '../utils/priceTierUtils';
|
|
22
22
|
|
|
23
23
|
const PlanOfferingButtonHeight = '66px';
|
|
24
24
|
|
|
@@ -167,22 +167,28 @@ export function PlanOffering({
|
|
|
167
167
|
);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
const [
|
|
170
|
+
const [perUnitQuantityByFeature, setPerUnitQuantityByFeature] = useState<Record<string, number>>({});
|
|
171
|
+
const [selectedTierByFeature, setSelectedTierByFeature] = useState<Record<string, PriceTierFragment>>(
|
|
171
172
|
getSelectedTier(plan, billingPeriod, currentSubscription, {}, selectDefaultTierIndex),
|
|
172
173
|
);
|
|
173
174
|
|
|
174
175
|
useEffect(() => {
|
|
175
176
|
setSelectedTierByFeature(getSelectedTier(plan, billingPeriod, currentSubscription, selectedTierByFeature));
|
|
177
|
+
setPerUnitQuantityByFeature(getSelectedTierQuantityBeFeature(plan, billingPeriod, currentSubscription));
|
|
176
178
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
177
179
|
}, [billingPeriod, currentSubscription, plan]);
|
|
178
180
|
|
|
179
181
|
const onPlanButtonClick = (intentionType: SubscribeIntentionType) => {
|
|
180
|
-
const
|
|
182
|
+
const tierBillableFeatures: BillableFeature[] = Object.keys(selectedTierByFeature).map((featureId) => ({
|
|
181
183
|
featureId,
|
|
182
|
-
quantity: selectedTierByFeature[featureId].upTo,
|
|
184
|
+
quantity: selectedTierByFeature[featureId].upTo || 0,
|
|
185
|
+
}));
|
|
186
|
+
const perUnitBillableFeatures: BillableFeature[] = Object.keys(perUnitQuantityByFeature).map((featureId) => ({
|
|
187
|
+
featureId,
|
|
188
|
+
quantity: perUnitQuantityByFeature[featureId],
|
|
183
189
|
}));
|
|
184
190
|
|
|
185
|
-
return onPlanSelected(intentionType,
|
|
191
|
+
return onPlanSelected(intentionType, { ...tierBillableFeatures, ...perUnitBillableFeatures });
|
|
186
192
|
};
|
|
187
193
|
|
|
188
194
|
return (
|
|
@@ -218,6 +224,8 @@ export function PlanOffering({
|
|
|
218
224
|
locale={locale}
|
|
219
225
|
hasAnnuallyPrice={hasAnnuallyPrice}
|
|
220
226
|
hasMonthlyPrice={hasMonthlyPrice}
|
|
227
|
+
setPerUnitQuantityByFeature={setPerUnitQuantityByFeature}
|
|
228
|
+
perUnitQuantityByFeature={perUnitQuantityByFeature}
|
|
221
229
|
/>
|
|
222
230
|
|
|
223
231
|
{showCTAButton ? (
|
|
@@ -232,6 +240,7 @@ export function PlanOffering({
|
|
|
232
240
|
paywallLocale={paywallLocale}
|
|
233
241
|
withTrialLeftRow={withTrialLeftRow}
|
|
234
242
|
selectedTierByFeature={selectedTierByFeature}
|
|
243
|
+
perUnitQuantityByFeature={perUnitQuantityByFeature}
|
|
235
244
|
/>
|
|
236
245
|
) : (
|
|
237
246
|
<div style={{ height: PlanOfferingButtonHeight }} />
|
|
@@ -83,6 +83,7 @@ type PlanOfferingButtonProps = {
|
|
|
83
83
|
onPlanSelected: (intentionType: SubscribeIntentionType) => void | Promise<void>;
|
|
84
84
|
withTrialLeftRow: boolean;
|
|
85
85
|
selectedTierByFeature: Record<string, PriceTierFragment>;
|
|
86
|
+
perUnitQuantityByFeature: Record<string, number>;
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
export function PlanOfferingButton({
|
|
@@ -96,6 +97,7 @@ export function PlanOfferingButton({
|
|
|
96
97
|
withTrialLeftRow,
|
|
97
98
|
currentSubscription,
|
|
98
99
|
selectedTierByFeature,
|
|
100
|
+
perUnitQuantityByFeature,
|
|
99
101
|
}: PlanOfferingButtonProps) {
|
|
100
102
|
const theme = useTheme();
|
|
101
103
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -142,7 +144,11 @@ export function PlanOfferingButton({
|
|
|
142
144
|
isSameBillingPeriod ||
|
|
143
145
|
(plan.pricingType && [PricingType.Free, PricingType.Custom].includes(plan.pricingType))
|
|
144
146
|
) {
|
|
145
|
-
const planComparison = compareSelectedTierToCurrentTier(
|
|
147
|
+
const planComparison = compareSelectedTierToCurrentTier(
|
|
148
|
+
selectedTierByFeature,
|
|
149
|
+
perUnitQuantityByFeature,
|
|
150
|
+
currentSubscription,
|
|
151
|
+
);
|
|
146
152
|
switch (planComparison) {
|
|
147
153
|
case PriceTierComparison.Lower:
|
|
148
154
|
buttonProps.intentionType = SubscribeIntentionType.CHANGE_UNIT_QUANTITY;
|