Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 17a6791

Browse files
authored
fix: growth plan upgrades (#2157)
* fix: growth plan upgrades * fix: billing alert labels
1 parent 4b35454 commit 17a6791

File tree

7 files changed

+128
-130
lines changed

7 files changed

+128
-130
lines changed

components/homepage/sections/PricingCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ export const PricingCard: React.FC<PricingCardProps> = ({
124124
<Text
125125
textAlign="center"
126126
size="body.sm"
127-
position={{ base: "static", md: "absolute" }}
128-
left={12}
127+
w="full"
128+
position={{ base: "static", xl: "absolute" }}
129129
top={ctaTitle ? 14 : -9}
130130
>
131131
{ctaHint}

components/onboarding/Modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const OnboardingModal: ComponentWithChildren<OnboardingModalProps> = ({
3030
closeOnEsc={false}
3131
allowPinchZoom
3232
closeOnOverlayClick={false}
33-
isCentered
33+
isCentered={!isMobile}
3434
isOpen={isOpen}
3535
onClose={onClose}
3636
trapFocus={false}

components/settings/Account/Billing/Alert.tsx

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ import {
1414
} from "@chakra-ui/react";
1515
import { useTrack } from "hooks/analytics/useTrack";
1616
import { useLocalStorage } from "hooks/useLocalStorage";
17-
import { useRouter } from "next/router";
1817
import React, { useCallback, useMemo } from "react";
1918
import { FiX } from "react-icons/fi";
2019

21-
import { TrackedLink, Text } from "tw-components";
20+
import { Heading, Text, TrackedLinkButton } from "tw-components";
2221

2322
enum DismissedStorageType {
2423
Usage_50 = "usage_50",
@@ -132,33 +131,32 @@ export const BillingAlert = () => {
132131
return null;
133132
}
134133

135-
const { status, stripePaymentActionUrl, paymentAttemptCount } = meQuery.data;
134+
const { status, stripePaymentActionUrl } = meQuery.data;
136135

137136
if (status === "paymentVerification") {
138-
const message = !stripePaymentActionUrl?.startsWith(
137+
const message = stripePaymentActionUrl?.startsWith(
139138
"https://payments.stripe.com/microdeposit",
140139
)
141-
? "To verify your bank account, we've deposited $0.01 and it should arrive within 1-2 working days. Once you receive it"
142-
: "Your card requires further verification. To proceed";
140+
? "To verify your bank account, we've deposited $0.01 and it should arrive within 1-2 working days."
141+
: "Your card requires further verification.";
143142

144143
return (
145144
<BillingTypeAlert
146145
title="Your payment method is not verified"
147146
description={message}
148147
status="warning"
149-
ctaText="verify your payment method"
148+
label="verifyPaymentAlert"
149+
ctaHref={stripePaymentActionUrl}
150+
ctaText="Verify your payment method"
150151
/>
151152
);
152153
}
153154

154-
if (
155-
status === AccountStatus.InvalidPayment ||
156-
(paymentAttemptCount && paymentAttemptCount !== 0)
157-
) {
155+
if (status === AccountStatus.InvalidPayment) {
158156
return (
159157
<BillingTypeAlert
160158
title="Your payment method was declined"
161-
description="You have an overdue invoice. To continue using thirdweb services without interruption, please"
159+
description="You have an overdue invoice. To continue using thirdweb services without interruption, please add your payment method."
162160
status="error"
163161
/>
164162
);
@@ -197,9 +195,10 @@ export const BillingAlert = () => {
197195
return (
198196
<BillingTypeAlert
199197
title="You have exceeded your RPC rate limit"
200-
description={`You have exceeded your RPC rate limit (${usageQuery.data.rateLimits.rpc} requests per second). Please add your payment method and upgrade your plan to continue using thirdweb services without interruption. You can upgrade to thirdweb Growth by visiting your`}
201-
ctaText="Billing Settings"
198+
description={`You have exceeded your RPC rate limit (${usageQuery.data.rateLimits.rpc} requests per second). Please add your payment method and upgrade your plan to continue using thirdweb services without interruption. You can upgrade to thirdweb Growth by visiting your Billing settings.`}
199+
ctaText="Go to Billing"
202200
status="warning"
201+
label="exceededRpcLimitAlert"
203202
onDismiss={() => handleDismiss(DismissedStorageType.RateRpc)}
204203
/>
205204
);
@@ -212,9 +211,10 @@ export const BillingAlert = () => {
212211
return (
213212
<BillingTypeAlert
214213
title="You have exceeded your Storage Gateway rate limit"
215-
description={`You have exceeded your Storage Gateway rate limit (${usageQuery.data.rateLimits.storage} requests per second). Please add your payment method and upgrade your plan to continue using thirdweb services without interruption. You can upgrade to thirdweb Growth by visiting your`}
216-
ctaText="Billing Settings"
214+
description={`You have exceeded your Storage Gateway rate limit (${usageQuery.data.rateLimits.storage} requests per second). Please add your payment method and upgrade your plan to continue using thirdweb services without interruption. You can upgrade to thirdweb Growth by visiting your Billing settings.`}
215+
ctaText="Go to Billing"
217216
status="warning"
217+
label="exceededGatewayLimitAlert"
218218
onDismiss={() => handleDismiss(DismissedStorageType.RateStorage)}
219219
/>
220220
);
@@ -230,18 +230,18 @@ type BillingTypeAlertProps = {
230230
description?: string;
231231
ctaText?: string;
232232
ctaHref?: string;
233+
label?: string;
233234
};
234235

235236
const BillingTypeAlert: React.FC<BillingTypeAlertProps> = ({
236237
status,
237238
onDismiss,
238239
title,
239-
description = "To ensure there are no future interruptions to your services",
240-
ctaText = "add a payment method",
240+
description = "To ensure there are no future interruptions to your services, please add your payment method.",
241+
ctaText = "Add a payment method",
241242
ctaHref = "/dashboard/settings/billing",
243+
label = "addPaymentAlert",
242244
}) => {
243-
const router = useRouter();
244-
245245
return (
246246
<Alert
247247
status={status}
@@ -256,27 +256,23 @@ const BillingTypeAlert: React.FC<BillingTypeAlertProps> = ({
256256
<Flex>
257257
<AlertIcon boxSize={4} mt={1} ml={1} />
258258
<Flex flexDir="column" gap={1} pl={1}>
259-
<AlertTitle>{title}</AlertTitle>
260-
<AlertDescription>
261-
<Text size="body.md" as="span" pr={1}>
262-
{description}
263-
</Text>
264-
{router.pathname.startsWith(ctaHref) ? (
265-
<Text as="span">{ctaText}</Text>
266-
) : (
267-
<TrackedLink
268-
href={ctaHref}
269-
category="billing"
270-
label="limit_exceeded"
271-
fontWeight="medium"
272-
color="blue.500"
273-
>
274-
<Text as="span" color="blue.500">
275-
{ctaText}
276-
</Text>
277-
</TrackedLink>
278-
)}
279-
.
259+
<AlertTitle>
260+
<Heading as="span" size="subtitle.sm">
261+
{title}.{" "}
262+
</Heading>
263+
<Text as="span">{description}</Text>
264+
</AlertTitle>
265+
<AlertDescription my={2}>
266+
<TrackedLinkButton
267+
href={ctaHref}
268+
category="billing"
269+
label={label}
270+
fontWeight="medium"
271+
colorScheme="blue"
272+
size="sm"
273+
>
274+
{ctaText}
275+
</TrackedLinkButton>
280276
</AlertDescription>
281277
</Flex>
282278
</Flex>
Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { useTrack } from "hooks/analytics/useTrack";
2-
import { Button } from "tw-components";
1+
import { TrackedLinkButton } from "tw-components";
32
import { MouseEvent, useEffect, useState } from "react";
43
import {
54
Account,
@@ -20,42 +19,20 @@ export const ManageBillingButton: React.FC<ManageBillingButtonProps> = ({
2019
loadingText,
2120
onClick,
2221
}) => {
23-
const trackEvent = useTrack();
2422
const [sessionUrl, setSessionUrl] = useState();
25-
const paymentVerification =
26-
account?.status === AccountStatus.PaymentVerification &&
27-
account.stripePaymentActionUrl;
28-
const validPayment =
29-
account?.status === AccountStatus.ValidPayment &&
30-
!account.paymentAttemptCount;
31-
3223
const mutation = useCreateBillingSession();
3324

34-
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
35-
e.preventDefault();
36-
37-
if (paymentVerification) {
38-
window.open(account.stripePaymentActionUrl);
39-
40-
trackEvent({
41-
category: "billingAccount",
42-
action: "click",
43-
label: "verifyPaymentMethod",
44-
});
25+
const validPayment = account.status === AccountStatus.ValidPayment;
26+
const paymentVerification =
27+
account.status === AccountStatus.PaymentVerification;
4528

46-
return;
47-
} else if (onClick) {
48-
onClick();
49-
return;
29+
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
30+
if (onClick || loading || (!sessionUrl && !paymentVerification)) {
31+
e.preventDefault();
32+
if (onClick) {
33+
onClick();
34+
}
5035
}
51-
52-
window.open(sessionUrl);
53-
54-
trackEvent({
55-
category: "billingAccount",
56-
action: "click",
57-
label: "manage",
58-
});
5936
};
6037

6138
useEffect(() => {
@@ -70,21 +47,29 @@ export const ManageBillingButton: React.FC<ManageBillingButtonProps> = ({
7047
}, [paymentVerification]);
7148

7249
return (
73-
<Button
74-
variant="link"
50+
<TrackedLinkButton
51+
variant="outline"
7552
isDisabled={loading || (!sessionUrl && !paymentVerification)}
53+
href={account.stripePaymentActionUrl || sessionUrl || ""}
7654
isLoading={loading}
55+
category="billingAccount"
56+
label={
57+
paymentVerification
58+
? "verifyPaymentMethod"
59+
: onClick
60+
? "addPayment"
61+
: "manage"
62+
}
7763
loadingText={loadingText}
7864
onClick={handleClick}
79-
colorScheme={loading ? "gray" : "blue"}
80-
size="sm"
81-
fontWeight="normal"
65+
color={loading ? "gray" : "blue.500"}
66+
fontSize="small"
8267
>
8368
{validPayment
8469
? "Manage billing"
8570
: paymentVerification
8671
? "Verify payment method →"
8772
: "Add payment method →"}
88-
</Button>
73+
</TrackedLinkButton>
8974
);
9075
};

components/settings/Account/Billing/Pricing.tsx

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
import { SimpleGrid } from "@chakra-ui/react";
2-
import { Account, AccountPlan } from "@3rdweb-sdk/react/hooks/useApi";
2+
import { AccountPlan } from "@3rdweb-sdk/react/hooks/useApi";
33
import { PricingCard } from "components/homepage/sections/PricingCard";
44
import { useMemo } from "react";
55
import { CONTACT_US_URL } from "utils/pricing";
66
import { remainingDays } from "utils/date-utils";
77

88
interface BillingPricingProps {
9-
account: Account;
10-
onSelect: (plan: AccountPlan) => void;
9+
plan: AccountPlan;
1110
validPayment: boolean;
11+
paymentVerification: boolean;
12+
invalidPayment: boolean;
1213
loading: boolean;
1314
canTrialGrowth?: boolean;
15+
trialPeriodEndedAt?: string;
16+
onSelect: (plan: AccountPlan) => void;
1417
}
1518

1619
export const BillingPricing: React.FC<BillingPricingProps> = ({
17-
account,
18-
onSelect,
20+
plan,
1921
validPayment,
22+
paymentVerification,
23+
invalidPayment,
24+
trialPeriodEndedAt,
2025
loading,
2126
canTrialGrowth,
27+
onSelect,
2228
}) => {
23-
const isPro = [AccountPlan.Pro, AccountPlan.Enterprise].includes(
24-
account.plan,
25-
);
29+
const isPro = [AccountPlan.Pro, AccountPlan.Enterprise].includes(plan);
2630

2731
const freeCtaTitle = useMemo(() => {
2832
if (!validPayment) {
29-
return "Add payment method";
33+
return "Get started for free";
3034
}
31-
if (account.plan !== AccountPlan.Free) {
35+
if (plan !== AccountPlan.Free) {
3236
return "Downgrade";
3337
}
34-
}, [account, validPayment]);
38+
}, [plan, validPayment]);
3539

3640
const growthCtaTitle = useMemo(() => {
3741
const trialTitle = "Claim your 1-month free";
@@ -44,10 +48,10 @@ export const BillingPricing: React.FC<BillingPricingProps> = ({
4448
return "Contact us";
4549
}
4650

47-
if (account.plan === AccountPlan.Free) {
51+
if (plan === AccountPlan.Free) {
4852
return canTrialGrowth ? trialTitle : "Upgrade";
4953
}
50-
}, [account, validPayment, isPro, canTrialGrowth]);
54+
}, [validPayment, isPro, plan, canTrialGrowth]);
5155

5256
const trialPeriodDays = useMemo(() => {
5357
let days = undefined;
@@ -57,28 +61,25 @@ export const BillingPricing: React.FC<BillingPricingProps> = ({
5761
days = 30;
5862
}
5963
// already has trial period
60-
else if (
61-
account.trialPeriodEndedAt &&
62-
account.plan === AccountPlan.Growth
63-
) {
64-
days = remainingDays(account.trialPeriodEndedAt);
64+
else if (trialPeriodEndedAt && plan === AccountPlan.Growth) {
65+
days = remainingDays(trialPeriodEndedAt);
6566
}
6667

6768
if (!days) {
6869
return undefined;
6970
}
7071

7172
return `Your free trial will end in ${days} days.`;
72-
}, [account, canTrialGrowth, isPro]);
73+
}, [canTrialGrowth, isPro, plan, trialPeriodEndedAt]);
7374

74-
const handleSelect = (plan: AccountPlan) => {
75-
onSelect(plan);
75+
const handleSelect = (newPlan: AccountPlan) => {
76+
onSelect(newPlan);
7677
};
7778

7879
return (
7980
<SimpleGrid columns={{ base: 1, xl: 3 }} gap={{ base: 6, xl: 8 }}>
8081
<PricingCard
81-
current={account.plan === AccountPlan.Free}
82+
current={plan === AccountPlan.Free}
8283
size="sm"
8384
name={AccountPlan.Free}
8485
ctaTitle={freeCtaTitle}
@@ -88,15 +89,15 @@ export const BillingPricing: React.FC<BillingPricingProps> = ({
8889
handleSelect(AccountPlan.Free);
8990
},
9091
isLoading: loading,
91-
isDisabled: loading,
92+
isDisabled: loading || invalidPayment || paymentVerification,
9293
category: "account",
9394
label: "freePlan",
9495
href: "/pricing",
9596
}}
9697
/>
9798

9899
<PricingCard
99-
current={account.plan === AccountPlan.Growth}
100+
current={plan === AccountPlan.Growth}
100101
size="sm"
101102
name={AccountPlan.Growth}
102103
ctaTitle={growthCtaTitle}
@@ -108,7 +109,7 @@ export const BillingPricing: React.FC<BillingPricingProps> = ({
108109
handleSelect(AccountPlan.Growth);
109110
},
110111
isLoading: loading,
111-
isDisabled: loading,
112+
isDisabled: loading || invalidPayment || paymentVerification,
112113
category: "account",
113114
label: "growthPlan",
114115
href: "/pricing",

0 commit comments

Comments
 (0)