@stigg/react-sdk 4.1.2 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.1.2",
2
+ "version": "4.2.1",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -85,7 +85,7 @@
85
85
  "@emotion/react": "^11.10.5",
86
86
  "@emotion/styled": "^11.10.5",
87
87
  "@mui/material": "^5.10.13",
88
- "@stigg/js-client-sdk": "2.15.0",
88
+ "@stigg/js-client-sdk": "2.18.1",
89
89
  "@types/styled-components": "^5.1.26",
90
90
  "classnames": "^2.3.2",
91
91
  "color": "^4.2.3",
@@ -1,4 +1,4 @@
1
- import { BillingPeriod, Customer, Plan, Subscription } from '@stigg/js-client-sdk';
1
+ import { BillableFeature, BillingPeriod, Customer, Plan, Subscription } from '@stigg/js-client-sdk';
2
2
  import React, { useCallback } from 'react';
3
3
  import styled from '@emotion/styled/macro';
4
4
  import { PlanOffering } from './PlanOffering';
@@ -11,6 +11,7 @@ import { useStiggContext } from '../..';
11
11
  import { hasPricePointsForPlans } from './utils/hasPricePoints';
12
12
  import { getPlansToDisplay } from './utils/getPlansToDisplay';
13
13
  import { getPlanPrice } from '../utils/getPlanPrice';
14
+ import { getSelectedTier } from './planPriceTier';
14
15
 
15
16
  const PaywallPlansContainer = styled.div`
16
17
  color: ${({ theme }) => theme.stigg.palette.text.primary};
@@ -74,13 +75,14 @@ export const Paywall = ({
74
75
  const plansToShow = getPlansToDisplay(plans, selectedBillingPeriod);
75
76
 
76
77
  const handleOnSubscribe = useCallback(
77
- (plan: Plan, intentionType: SubscribeIntentionType) => {
78
+ (plan: Plan, intentionType: SubscribeIntentionType, billableFeatures: BillableFeature[]) => {
78
79
  return onPlanSelected({
79
80
  plan,
80
81
  customer,
81
82
  subscription: currentSubscription,
82
83
  intentionType,
83
84
  selectedBillingPeriod,
85
+ billableFeatures,
84
86
  });
85
87
  },
86
88
  [customer, selectedBillingPeriod],
@@ -98,6 +100,14 @@ export const Paywall = ({
98
100
  return !!getPlanPrice(plan, selectedBillingPeriod, paywallLocale, locale, hasMonthlyPrice).unit;
99
101
  });
100
102
 
103
+ const withTiersRow = plansToShow.some(plan => {
104
+ return !!getSelectedTier(plan, selectedBillingPeriod, currentSubscription, {});
105
+ });
106
+
107
+ const withTrialLeftRow = plansToShow.some(plan => {
108
+ return plan.isCurrentCustomerPlan && plan.trialDaysLeft;
109
+ });
110
+
101
111
  return (
102
112
  <PaywallContainer className="stigg-paywall-container">
103
113
  <PaywallLayout className="stigg-paywall-layout">
@@ -112,6 +122,8 @@ export const Paywall = ({
112
122
  {plansToShow.map(plan => (
113
123
  <PlanOffering
114
124
  withUnitPriceRow={withUnitPriceRow}
125
+ withTiersRow={withTiersRow}
126
+ withTrialLeftRow={withTrialLeftRow}
115
127
  key={plan.id}
116
128
  shouldShowDescriptionSection={shouldShowDescriptionSection}
117
129
  hasMonthlyPrice={hasMonthlyPrice}
@@ -122,7 +134,9 @@ export const Paywall = ({
122
134
  billingPeriod={selectedBillingPeriod}
123
135
  isHighlighted={plan.id === highlightedPlanId}
124
136
  isCustomerOnTrial={isCustomerOnTrial}
125
- onPlanSelected={(intentionType: SubscribeIntentionType) => handleOnSubscribe(plan, intentionType)}
137
+ onPlanSelected={(intentionType: SubscribeIntentionType, billableFeatures: BillableFeature[]) =>
138
+ handleOnSubscribe(plan, intentionType, billableFeatures)
139
+ }
126
140
  paywallLocale={paywallLocale}
127
141
  locale={locale}
128
142
  customer={customer}
@@ -1,7 +1,6 @@
1
- import React from 'react';
2
- import { BillingPeriod, Customer, Subscription } from '@stigg/js-client-sdk';
1
+ import React, { useEffect } from 'react';
2
+ import { BillableFeature, BillingPeriod, Customer, PriceTierFragment, Subscription } from '@stigg/js-client-sdk';
3
3
  import styled from '@emotion/styled/macro';
4
- import { getPlanPrice } from '../utils/getPlanPrice';
5
4
  import { PlanEntitlements } from './PlanEntitlements';
6
5
  import { PlanOfferingButton } from './PlanOfferingButton';
7
6
  import { PaywallPlan, SubscribeIntentionType } from './types';
@@ -11,8 +10,8 @@ import { Typography } from '../common/Typography';
11
10
  import classNames from 'classnames';
12
11
  import { Grid } from '@mui/material';
13
12
  import MiniSchedule from '../../assets/mini-schedule.svg';
14
-
15
- const EMPTY_CHAR = '';
13
+ import { PlanPrice } from './PlanPrice';
14
+ import { getSelectedTier } from './planPriceTier';
16
15
 
17
16
  const PlanOfferingContainer = styled.div<{ $isHighlighted: boolean; $isCurrentPlan: boolean }>`
18
17
  position: relative;
@@ -35,99 +34,6 @@ const PlanHeader = styled(Typography)`
35
34
  padding-bottom: 8px;
36
35
  `;
37
36
 
38
- const PlanPriceContainer = styled(Typography)`
39
- word-break: break-word;
40
- `;
41
-
42
- const UnitSpan = styled(Typography)`
43
- white-space: nowrap;
44
- `;
45
-
46
- const PriceSpan = styled(Typography)`
47
- white-space: normal;
48
- min-height: 39px;
49
- `;
50
-
51
- function PlanPrice({
52
- showStartingAt,
53
- withUnitPriceRow,
54
- withStartingAtRow,
55
- plan,
56
- billingPeriod,
57
- paywallLocale,
58
- locale,
59
- hasMonthlyPrice,
60
- hasAnnuallyPrice,
61
- }: {
62
- showStartingAt: boolean;
63
- withUnitPriceRow: boolean;
64
- withStartingAtRow: boolean;
65
- plan: PaywallPlan;
66
- billingPeriod: BillingPeriod;
67
- paywallLocale: PaywallLocalization;
68
- hasMonthlyPrice: boolean;
69
- hasAnnuallyPrice: boolean;
70
- locale: string;
71
- }) {
72
- const { price, unit } = getPlanPrice(plan, billingPeriod, paywallLocale, locale, hasMonthlyPrice);
73
-
74
- return (
75
- <PlanPriceContainer className="stigg-price-text">
76
- <>
77
- {withStartingAtRow && (
78
- <Typography
79
- style={{ minHeight: '20px' }}
80
- className="stigg-starting-at-text"
81
- variant="body1"
82
- color="secondary"
83
- >
84
- {showStartingAt ? paywallLocale.price.startingAtCaption : EMPTY_CHAR}
85
- </Typography>
86
- )}
87
-
88
- <PriceSpan variant="h1">{price}</PriceSpan>
89
-
90
- {withUnitPriceRow && (
91
- <Typography className="stigg-price-unit-and-billing-period-text" style={{ minHeight: '48px' }}>
92
- <UnitSpan span variant="h6" color="secondary">
93
- {unit}
94
- </UnitSpan>
95
-
96
- <PriceBillingPeriod
97
- plan={plan}
98
- billingPeriod={billingPeriod}
99
- hasAnnuallyPrice={hasAnnuallyPrice}
100
- hasMonthlyPrice={hasMonthlyPrice}
101
- />
102
- </Typography>
103
- )}
104
- </>
105
- </PlanPriceContainer>
106
- );
107
- }
108
-
109
- type PriceBillingPeriodProps = {
110
- plan: PaywallPlan;
111
- billingPeriod: BillingPeriod;
112
- hasMonthlyPrice: boolean;
113
- hasAnnuallyPrice: boolean;
114
- };
115
-
116
- function PriceBillingPeriod({ plan, billingPeriod, hasMonthlyPrice, hasAnnuallyPrice }: PriceBillingPeriodProps) {
117
- const hasPrice = plan.pricePoints.find(pricePoint => pricePoint.billingPeriod === billingPeriod);
118
-
119
- let content = EMPTY_CHAR;
120
- if (hasPrice && hasMonthlyPrice && hasAnnuallyPrice) {
121
- content = `, billed ${billingPeriod.toLowerCase()}`;
122
- }
123
-
124
- return (
125
- <Typography span className="stigg-price-billing-period-text" variant="h6" color="secondary">
126
- {content}
127
- </Typography>
128
- );
129
- }
130
-
131
37
  const Divider = styled.div`
132
38
  height: 2px;
133
39
  width: 100%;
@@ -161,6 +67,8 @@ const HeaderWrapper = styled.div`
161
67
 
162
68
  type PlanOfferingProps = {
163
69
  withUnitPriceRow: boolean;
70
+ withTiersRow: boolean;
71
+ withTrialLeftRow: boolean;
164
72
  customer: Customer | null;
165
73
  plan: PaywallPlan;
166
74
  billingPeriod: BillingPeriod;
@@ -170,10 +78,10 @@ type PlanOfferingProps = {
170
78
  hasAnnuallyPrice: boolean;
171
79
  hasMonthlyPrice: boolean;
172
80
  isCustomerOnTrial: boolean;
173
- onPlanSelected: (intentionType: SubscribeIntentionType) => void | Promise<void>;
81
+ onPlanSelected: (intentionType: SubscribeIntentionType, billableFeatures: BillableFeature[]) => void | Promise<void>;
174
82
  paywallLocale: PaywallLocalization;
175
83
  locale: string;
176
- withStartingAtRow?: boolean;
84
+ withStartingAtRow: boolean;
177
85
  };
178
86
 
179
87
  const NextPlanTagContainer = styled.div`
@@ -187,7 +95,7 @@ const NextPlanTagContainer = styled.div`
187
95
  const StyledMiniSchedule = styled(MiniSchedule)<{ $iconsColor?: string }>`
188
96
  display: flex;
189
97
  height: 100%;
190
- margin-right: 4;
98
+ margin-right: 4px;
191
99
 
192
100
  path {
193
101
  fill: ${({ theme }) => theme.stigg.palette.text.primary};
@@ -209,6 +117,8 @@ function UpcomingChangeTag({ text = 'Next', iconsColor }: { text?: string; icons
209
117
 
210
118
  export function PlanOffering({
211
119
  withUnitPriceRow,
120
+ withTiersRow,
121
+ withTrialLeftRow,
212
122
  customer,
213
123
  plan,
214
124
  billingPeriod,
@@ -243,6 +153,23 @@ export function PlanOffering({
243
153
  );
244
154
  }
245
155
 
156
+ const [selectedTierByFeature, setSelectedTierByFeature] = React.useState<Record<string, PriceTierFragment>>(
157
+ getSelectedTier(plan, billingPeriod, currentSubscription, {}),
158
+ );
159
+
160
+ useEffect(() => {
161
+ setSelectedTierByFeature(getSelectedTier(plan, billingPeriod, currentSubscription, selectedTierByFeature));
162
+ }, [billingPeriod]);
163
+
164
+ const onPlanButtonClick = (intentionType: SubscribeIntentionType) => {
165
+ const billableFeatures: BillableFeature[] = Object.keys(selectedTierByFeature).map(featureId => ({
166
+ featureId,
167
+ quantity: selectedTierByFeature[featureId].upTo,
168
+ }));
169
+
170
+ return onPlanSelected(intentionType, billableFeatures);
171
+ };
172
+
246
173
  return (
247
174
  <PlanOfferingContainer
248
175
  className={classNames('stigg-plan-offering-container', {
@@ -266,8 +193,11 @@ export function PlanOffering({
266
193
 
267
194
  <PlanPrice
268
195
  showStartingAt={showStartingAt}
269
- withUnitPriceRow={!!withUnitPriceRow}
270
- withStartingAtRow={!!withStartingAtRow}
196
+ withUnitPriceRow={withUnitPriceRow}
197
+ withStartingAtRow={withStartingAtRow}
198
+ withTiersRow={withTiersRow}
199
+ selectedTierByFeature={selectedTierByFeature}
200
+ setSelectedTierByFeature={setSelectedTierByFeature}
271
201
  plan={plan}
272
202
  billingPeriod={billingPeriod}
273
203
  paywallLocale={paywallLocale}
@@ -283,8 +213,11 @@ export function PlanOffering({
283
213
  currentSubscription={currentSubscription}
284
214
  billingPeriod={billingPeriod}
285
215
  isCustomerOnTrial={isCustomerOnTrial}
286
- onPlanSelected={onPlanSelected}
216
+ onPlanSelected={onPlanButtonClick}
287
217
  paywallLocale={paywallLocale}
218
+ hasTiersRow={withTiersRow}
219
+ withTrialLeftRow={withTrialLeftRow}
220
+ selectedTierByFeature={selectedTierByFeature}
288
221
  />
289
222
 
290
223
  <Divider className="stigg-plan-header-divider" />
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from 'react';
2
- import { BillingPeriod, Customer, PricingType, Subscription } from '@stigg/js-client-sdk';
2
+ import { BillingPeriod, Customer, PriceTierFragment, PricingType, Subscription } from '@stigg/js-client-sdk';
3
3
  import styled from '@emotion/styled/macro';
4
4
  import { css, Theme, useTheme } from '@emotion/react';
5
5
  import { PaywallPlan, SubscribeIntentionType } from './types';
@@ -9,6 +9,7 @@ import ClipLoader from 'react-spinners/ClipLoader';
9
9
  import { Typography } from '../common/Typography';
10
10
  import { getSubscriptionScheduleUpdateTexts } from '../utils/getSubscriptionScheduleUpdateTexts';
11
11
  import { isFunction } from 'lodash';
12
+ import { compareSelectedTierToCurrentTier, PriceTierComparison } from './planPriceTier';
12
13
 
13
14
  const LoadingIndicator = styled(ClipLoader)`
14
15
  margin-left: 10px;
@@ -66,7 +67,7 @@ const TrialDaysLeft = styled(Typography)`
66
67
  const ButtonLayout = styled.div`
67
68
  display: flex;
68
69
  flex-direction: column;
69
- margin-top: 40px;
70
+ margin-top: 4px;
70
71
  align-self: ${({ theme }) => flexLayoutMapper(theme.stigg.layout.ctaAlignment)};
71
72
  width: 100%;
72
73
  `;
@@ -80,6 +81,9 @@ type PlanOfferingButtonProps = {
80
81
  isCustomerOnTrial: boolean;
81
82
  paywallLocale: PaywallLocalization;
82
83
  onPlanSelected: (intentionType: SubscribeIntentionType) => void | Promise<void>;
84
+ hasTiersRow: boolean;
85
+ withTrialLeftRow: boolean;
86
+ selectedTierByFeature: Record<string, PriceTierFragment>;
83
87
  };
84
88
 
85
89
  export function PlanOfferingButton({
@@ -90,6 +94,10 @@ export function PlanOfferingButton({
90
94
  isCustomerOnTrial,
91
95
  onPlanSelected,
92
96
  paywallLocale,
97
+ hasTiersRow,
98
+ withTrialLeftRow,
99
+ currentSubscription,
100
+ selectedTierByFeature,
93
101
  }: PlanOfferingButtonProps) {
94
102
  const theme = useTheme() as Theme;
95
103
  const [isLoading, setIsLoading] = useState(false);
@@ -136,8 +144,22 @@ export function PlanOfferingButton({
136
144
  isSameBillingPeriod ||
137
145
  (plan.pricingType && [PricingType.Free, PricingType.Custom].includes(plan.pricingType))
138
146
  ) {
139
- buttonProps.title = currentPlan;
140
- buttonProps.disabled = true;
147
+ const planComparison = compareSelectedTierToCurrentTier(selectedTierByFeature, currentSubscription);
148
+ switch (planComparison) {
149
+ case PriceTierComparison.Lower:
150
+ buttonProps.intentionType = SubscribeIntentionType.CHANGE_UNIT_QUANTITY;
151
+ buttonProps.title = resolvedDowngrade;
152
+ break;
153
+ case PriceTierComparison.Higher:
154
+ buttonProps.intentionType = SubscribeIntentionType.CHANGE_UNIT_QUANTITY;
155
+ buttonProps.title = resolvedUpgrade;
156
+ break;
157
+
158
+ default:
159
+ buttonProps.title = currentPlan;
160
+ buttonProps.disabled = true;
161
+ break;
162
+ }
141
163
  } else {
142
164
  buttonProps.title = switchToBillingPeriod(billingPeriod);
143
165
  buttonProps.intentionType = SubscribeIntentionType.CHANGE_BILLING_PERIOD;
@@ -188,13 +210,17 @@ export function PlanOfferingButton({
188
210
  {isLoading && <LoadingIndicator color={theme.stigg.palette.text.disabled} loading size={16} />}
189
211
  </OfferingButton>
190
212
 
191
- <TrialDaysLeft className="stigg-trial-days-left-text" variant="h6" color="secondary">
192
- {plan.isCurrentCustomerPlan && plan.trialDaysLeft && (
193
- <>
194
- <b>{plan.trialDaysLeft} days</b> left on your free trial
195
- </>
196
- )}
197
- </TrialDaysLeft>
213
+ {hasTiersRow && !withTrialLeftRow ? (
214
+ <div style={{ height: '20px' }} />
215
+ ) : (
216
+ <TrialDaysLeft className="stigg-trial-days-left-text" variant="h6" color="secondary">
217
+ {plan.isCurrentCustomerPlan && plan.trialDaysLeft && (
218
+ <>
219
+ <b>{plan.trialDaysLeft} days</b> left on your free trial
220
+ </>
221
+ )}
222
+ </TrialDaysLeft>
223
+ )}
198
224
  </>
199
225
  )}
200
226
  </ButtonLayout>
@@ -0,0 +1,152 @@
1
+ import { PaywallPlan } from './types';
2
+ import { PaywallLocalization } from './paywallTextOverrides';
3
+ import { getPlanPrice } from '../utils/getPlanPrice';
4
+ import { Typography } from '../common/Typography';
5
+ import React, { useEffect, useState } from 'react';
6
+ import styled from '@emotion/styled/macro';
7
+ import { BillingPeriod, PriceTierFragment } from '@stigg/js-client-sdk';
8
+ import { TiersLayout } from './TiersLayout';
9
+
10
+ const EMPTY_CHAR = '‎';
11
+
12
+ const PlanPriceContainer = styled(Typography)`
13
+ word-break: break-word;
14
+ width: 100%;
15
+ `;
16
+
17
+ const UnitSpan = styled(Typography)`
18
+ white-space: nowrap;
19
+ `;
20
+
21
+ const PriceSpan = styled(Typography)`
22
+ white-space: normal;
23
+ min-height: 39px;
24
+ `;
25
+
26
+ type PriceBillingPeriodProps = {
27
+ plan: PaywallPlan;
28
+ billingPeriod: BillingPeriod;
29
+ hasMonthlyPrice: boolean;
30
+ hasAnnuallyPrice: boolean;
31
+ };
32
+
33
+ function PriceBillingPeriod({ plan, billingPeriod, hasMonthlyPrice, hasAnnuallyPrice }: PriceBillingPeriodProps) {
34
+ const hasPrice = plan.pricePoints.find(pricePoint => pricePoint.billingPeriod === billingPeriod);
35
+
36
+ let content = EMPTY_CHAR;
37
+ if (hasPrice && hasMonthlyPrice && hasAnnuallyPrice) {
38
+ content = `, billed ${billingPeriod.toLowerCase()}`;
39
+ }
40
+
41
+ return (
42
+ <Typography span className="stigg-price-billing-period-text" variant="h6" color="secondary">
43
+ {content}
44
+ </Typography>
45
+ );
46
+ }
47
+
48
+ export const PlanPrice = ({
49
+ showStartingAt,
50
+ withUnitPriceRow,
51
+ withStartingAtRow,
52
+ withTiersRow,
53
+ selectedTierByFeature,
54
+ setSelectedTierByFeature,
55
+ plan,
56
+ billingPeriod,
57
+ paywallLocale,
58
+ locale,
59
+ hasMonthlyPrice,
60
+ hasAnnuallyPrice,
61
+ }: {
62
+ showStartingAt: boolean;
63
+ withUnitPriceRow: boolean;
64
+ withStartingAtRow: boolean;
65
+ withTiersRow: boolean;
66
+ selectedTierByFeature: Record<string, PriceTierFragment>;
67
+ setSelectedTierByFeature: (selectedTierByFeature: Record<string, PriceTierFragment>) => void;
68
+ plan: PaywallPlan;
69
+ billingPeriod: BillingPeriod;
70
+ paywallLocale: PaywallLocalization;
71
+ hasMonthlyPrice: boolean;
72
+ hasAnnuallyPrice: boolean;
73
+ locale: string;
74
+ }) => {
75
+ const { price, unit, tiers, tierUnits } = getPlanPrice(
76
+ plan,
77
+ billingPeriod,
78
+ paywallLocale,
79
+ locale,
80
+ hasMonthlyPrice,
81
+ selectedTierByFeature,
82
+ );
83
+
84
+ const [selectedTier, setSelectedTier] = useState<PriceTierFragment>();
85
+
86
+ useEffect(() => {
87
+ setSelectedTier(selectedTierByFeature[featureId!]);
88
+ }, [selectedTierByFeature]);
89
+
90
+ // We currently only support prices with one tier - so we select the first one
91
+ const tieredPrice = plan.pricePoints.find(planPrice => {
92
+ return planPrice.billingPeriod === billingPeriod && planPrice.isTieredPrice;
93
+ });
94
+ const featureId = tieredPrice ? tieredPrice!.feature!.featureId : undefined;
95
+
96
+ const handleTierChange = (tier: PriceTierFragment) => {
97
+ const updatedTierByFeature: Record<string, PriceTierFragment> = {};
98
+ updatedTierByFeature[featureId!] = tier;
99
+
100
+ setSelectedTierByFeature(updatedTierByFeature);
101
+ };
102
+
103
+ useEffect(() => {
104
+ if (tiers) {
105
+ handleTierChange(selectedTierByFeature[featureId!]);
106
+ }
107
+ }, []);
108
+
109
+ return (
110
+ <PlanPriceContainer className="stigg-price-text">
111
+ <>
112
+ {withStartingAtRow && (
113
+ <Typography
114
+ style={{ minHeight: '20px' }}
115
+ className="stigg-starting-at-text"
116
+ variant="body1"
117
+ color="secondary"
118
+ >
119
+ {showStartingAt ? paywallLocale.price.startingAtCaption : EMPTY_CHAR}
120
+ </Typography>
121
+ )}
122
+
123
+ <PriceSpan variant="h1">{price}</PriceSpan>
124
+
125
+ {withUnitPriceRow && (
126
+ <Typography className="stigg-price-unit-and-billing-period-text" style={{ minHeight: '48px' }}>
127
+ <UnitSpan span variant="h6" color="secondary">
128
+ {unit}
129
+ </UnitSpan>
130
+
131
+ <PriceBillingPeriod
132
+ plan={plan}
133
+ billingPeriod={billingPeriod}
134
+ hasAnnuallyPrice={hasAnnuallyPrice}
135
+ hasMonthlyPrice={hasMonthlyPrice}
136
+ />
137
+ </Typography>
138
+ )}
139
+
140
+ {withTiersRow && (
141
+ <TiersLayout
142
+ plan={plan}
143
+ tiers={tiers}
144
+ tierUnits={tierUnits}
145
+ selectedTier={selectedTier}
146
+ handleTierChange={handleTierChange}
147
+ />
148
+ )}
149
+ </>
150
+ </PlanPriceContainer>
151
+ );
152
+ };
@@ -0,0 +1,74 @@
1
+ import { PaywallPlan } from './types';
2
+ import { PriceTierFragment } from '@stigg/js-client-sdk';
3
+ import { MenuItem, OutlinedInput, Select, SelectChangeEvent } from '@mui/material';
4
+ import { Typography } from '../common/Typography';
5
+ import { map } from 'lodash';
6
+ import React, { ReactNode } from 'react';
7
+ import styled from '@emotion/styled/macro';
8
+
9
+ const TierSelect = styled(Select)`
10
+ border-radius: 10px;
11
+
12
+ &:hover .MuiOutlinedInput-notchedOutline {
13
+ border-color: ${({ theme }) => theme.stigg.palette.outlinedBorder};
14
+ }
15
+ `;
16
+
17
+ const TierInput = styled(OutlinedInput)`
18
+ & .MuiInputBase-input {
19
+ padding: 7px 12px;
20
+ }
21
+
22
+ &.Mui-focused .MuiOutlinedInput-notchedOutline {
23
+ border-color: ${({ theme }) => theme.stigg.palette.primary} !important;
24
+ }
25
+ `;
26
+
27
+ export function TiersLayout({
28
+ plan,
29
+ tiers,
30
+ tierUnits,
31
+ selectedTier,
32
+ handleTierChange,
33
+ }: {
34
+ plan: PaywallPlan;
35
+ tiers?: PriceTierFragment[] | null;
36
+ tierUnits?: string;
37
+ selectedTier?: PriceTierFragment;
38
+ handleTierChange: (tier: PriceTierFragment) => void;
39
+ }) {
40
+ const handleChange = (event: SelectChangeEvent<unknown>, _: ReactNode) => {
41
+ handleTierChange(tiers!.find(tier => tier.upTo.toString() === event.target.value)!);
42
+ };
43
+
44
+ return (
45
+ <Typography className="stigg-price-tier-select" style={{ minHeight: '46px' }}>
46
+ {tiers ? (
47
+ <TierSelect
48
+ labelId="demo-simple-select-label"
49
+ id={`select_${plan.id}`}
50
+ value={selectedTier ? selectedTier.upTo.toString() : tiers[0].upTo.toString()}
51
+ fullWidth
52
+ onChange={handleChange}
53
+ input={<TierInput />}
54
+ MenuProps={{
55
+ MenuListProps: { disablePadding: true },
56
+ PaperProps: {
57
+ sx: { marginTop: '4px', borderRadius: '10px' },
58
+ },
59
+ }}
60
+ >
61
+ {map(tiers, (tier: PriceTierFragment) => (
62
+ <MenuItem className="stigg-price-tier-menu-item-text" key={tier.upTo} value={tier.upTo.toString()}>
63
+ <Typography variant="body1" color="primary">
64
+ {tier.upTo} {tierUnits}
65
+ </Typography>
66
+ </MenuItem>
67
+ ))}
68
+ </TierSelect>
69
+ ) : (
70
+ <div />
71
+ )}
72
+ </Typography>
73
+ );
74
+ }
@@ -1,4 +1,11 @@
1
- import { BillingPeriod, PaywallCalculatedPricePoint, PaywallCurrency, Plan, Price } from '@stigg/js-client-sdk';
1
+ import {
2
+ BillingPeriod,
3
+ PaywallCalculatedPricePoint,
4
+ PaywallCurrency,
5
+ Plan,
6
+ Price,
7
+ PriceTierFragment,
8
+ } from '@stigg/js-client-sdk';
2
9
  import merge from 'lodash/merge';
3
10
  import { DeepPartial } from '../../types';
4
11
  import { PlanPriceText } from '../utils/getPlanPrice';
@@ -27,6 +34,7 @@ export type PaywallLocalization = {
27
34
  paywallCalculatedPrice?: PaywallCalculatedPricePoint;
28
35
  plan: Plan;
29
36
  selectedBillingPeriod: BillingPeriod;
37
+ selectedTier?: PriceTierFragment;
30
38
  }) => PlanPriceText;
31
39
  };
32
40
  };