Skip to content

Commit 3f57dc6

Browse files
committed
[Dashboard] update pricing plans and UI components
1 parent 0299034 commit 3f57dc6

File tree

9 files changed

+107
-258
lines changed

9 files changed

+107
-258
lines changed

apps/dashboard/src/@/components/blocks/pricing-card.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Badge } from "@/components/ui/badge";
44
import { Button } from "@/components/ui/button";
55
import { ToolTipLabel } from "@/components/ui/tooltip";
66
import { cn } from "@/lib/utils";
7-
import { CheckIcon, CircleDollarSignIcon } from "lucide-react";
7+
import { CheckIcon, DollarSignIcon } from "lucide-react";
88
import Link from "next/link";
99
import type React from "react";
1010
import { TEAM_PLANS } from "utils/pricing";
@@ -105,9 +105,12 @@ export const PricingCard: React.FC<PricingCardProps> = ({
105105

106106
{/* Price */}
107107
<div className="flex flex-col gap-0.5">
108+
{plan.isStartingPriceOnly && (
109+
<span className="text-muted-foreground text-xs">Starting at</span>
110+
)}
108111
<div className="flex items-center gap-2">
109112
<span className="font-semibold text-2xl text-foreground tracking-tight">
110-
${plan.price}
113+
${plan.price.toLocaleString()}
111114
</span>
112115

113116
{!isCustomPrice && (
@@ -183,10 +186,10 @@ const billingPlanToSkuMap: Record<Team["billingPlan"], ProductSKU | undefined> =
183186
{
184187
starter: "plan:starter",
185188
growth: "plan:growth",
186-
accelerate: "plan:accelerate",
187189
scale: "plan:scale",
190+
pro: "plan:pro",
188191
// we can't render checkout buttons for these plans:
189-
pro: undefined,
192+
accelerate: undefined,
190193
free: undefined,
191194
growth_legacy: undefined,
192195
starter_legacy: undefined,
@@ -201,16 +204,19 @@ function FeatureItem({ text }: FeatureItemProps) {
201204

202205
return (
203206
<div className="flex items-center gap-2 text-sm">
204-
<CheckIcon className="size-4 shrink-0 text-green-500" />
207+
{Array.isArray(text) ? (
208+
<ToolTipLabel label={text[1]}>
209+
<DollarSignIcon className="size-4 shrink-0 text-green-500" />
210+
</ToolTipLabel>
211+
) : (
212+
<CheckIcon className="size-4 shrink-0 text-green-500" />
213+
)}
205214
{Array.isArray(text) ? (
206215
<div className="flex items-center gap-2">
207216
<p className="text-muted-foreground">
208217
{titleStr}{" "}
209218
<span className="text-muted-foreground md:hidden">{text[1]}</span>
210219
</p>
211-
<ToolTipLabel label={text[1]}>
212-
<CircleDollarSignIcon className="hidden size-4 text-muted-foreground md:block" />
213-
</ToolTipLabel>
214220
</div>
215221
) : (
216222
<p className="text-muted-foreground">{titleStr}</p>

apps/dashboard/src/@/lib/billing.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type ProductSKU =
55
| "plan:custom"
66
| "plan:accelerate"
77
| "plan:scale"
8+
| "plan:pro"
89
| "product:ecosystem_wallets"
910
| "product:engine_standard"
1011
| "product:engine_premium"

apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.tsx

Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ export function InviteTeamMembersUI(props: {
9797
recommendedMembers={[]}
9898
customCTASection={
9999
<div className="flex gap-3">
100-
{props.team.billingPlan === "free" && (
100+
{(props.team.billingPlan === "free" ||
101+
props.team.billingPlan === "starter") && (
101102
<Button
102103
size="sm"
103104
variant="default"
@@ -154,30 +155,8 @@ function InviteModalContent(props: {
154155
getTeam: () => Promise<Team>;
155156
teamId: string;
156157
}) {
157-
const [planToShow, setPlanToShow] = useState<
158-
"starter" | "growth" | "accelerate" | "scale"
159-
>("growth");
160-
161-
const starterPlan = (
162-
<PricingCard
163-
billingPlan="starter"
164-
billingStatus={props.billingStatus}
165-
teamSlug={props.teamSlug}
166-
cta={{
167-
label: "Get Started",
168-
type: "checkout",
169-
onClick() {
170-
props.trackEvent({
171-
category: "teamOnboarding",
172-
action: "upgradePlan",
173-
label: "attempt",
174-
plan: "starter",
175-
});
176-
},
177-
}}
178-
getTeam={props.getTeam}
179-
teamId={props.teamId}
180-
/>
158+
const [planToShow, setPlanToShow] = useState<"growth" | "scale" | "pro">(
159+
"growth",
181160
);
182161

183162
const growthPlan = (
@@ -203,9 +182,9 @@ function InviteModalContent(props: {
203182
/>
204183
);
205184

206-
const acceleratePlan = (
185+
const scalePlan = (
207186
<PricingCard
208-
billingPlan="accelerate"
187+
billingPlan="scale"
209188
billingStatus={props.billingStatus}
210189
teamSlug={props.teamSlug}
211190
cta={{
@@ -216,7 +195,7 @@ function InviteModalContent(props: {
216195
category: "teamOnboarding",
217196
action: "upgradePlan",
218197
label: "attempt",
219-
plan: "accelerate",
198+
plan: "scale",
220199
});
221200
},
222201
}}
@@ -225,9 +204,9 @@ function InviteModalContent(props: {
225204
/>
226205
);
227206

228-
const scalePlan = (
207+
const proPlan = (
229208
<PricingCard
230-
billingPlan="scale"
209+
billingPlan="pro"
231210
billingStatus={props.billingStatus}
232211
teamSlug={props.teamSlug}
233212
cta={{
@@ -238,7 +217,7 @@ function InviteModalContent(props: {
238217
category: "teamOnboarding",
239218
action: "upgradePlan",
240219
label: "attempt",
241-
plan: "scale",
220+
plan: "pro",
242221
});
243222
},
244223
}}
@@ -266,43 +245,36 @@ function InviteModalContent(props: {
266245

267246
{/* Desktop */}
268247
<div className="hidden grid-cols-1 gap-6 md:grid-cols-4 md:gap-4 lg:grid">
269-
{starterPlan}
270248
{growthPlan}
271-
{acceleratePlan}
272249
{scalePlan}
250+
{proPlan}
273251
</div>
274252

275253
{/* Mobile */}
276254
<div className="lg:hidden">
277255
<TabButtons
278256
tabs={[
279-
{
280-
name: "Starter",
281-
onClick: () => setPlanToShow("starter"),
282-
isActive: planToShow === "starter",
283-
},
284257
{
285258
name: "Growth",
286259
onClick: () => setPlanToShow("growth"),
287260
isActive: planToShow === "growth",
288261
},
289-
{
290-
name: "Accelerate",
291-
onClick: () => setPlanToShow("accelerate"),
292-
isActive: planToShow === "accelerate",
293-
},
294262
{
295263
name: "Scale",
296264
onClick: () => setPlanToShow("scale"),
297265
isActive: planToShow === "scale",
298266
},
267+
{
268+
name: "Pro",
269+
onClick: () => setPlanToShow("pro"),
270+
isActive: planToShow === "pro",
271+
},
299272
]}
300273
/>
301274
<div className="h-4" />
302-
{planToShow === "starter" && starterPlan}
303275
{planToShow === "growth" && growthPlan}
304-
{planToShow === "accelerate" && acceleratePlan}
305276
{planToShow === "scale" && scalePlan}
277+
{planToShow === "pro" && proPlan}
306278
</div>
307279
</div>
308280
);

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/members/InviteSection.tsx

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ export function InviteSection(props: {
8585
let bottomSection: React.ReactNode = null;
8686
const maxAllowedInvitesAtOnce = 10;
8787
// invites are enabled if user has edit permission and team plan is not "free"
88-
const inviteEnabled = teamPlan !== "free" && props.userHasEditPermission;
88+
const inviteEnabled =
89+
teamPlan !== "free" &&
90+
teamPlan !== "starter" &&
91+
props.userHasEditPermission;
8992

9093
const form = useForm<InviteFormValues>({
9194
resolver: zodResolver(inviteFormSchema),
@@ -108,7 +111,7 @@ export function InviteSection(props: {
108111
},
109112
});
110113

111-
if (teamPlan === "free") {
114+
if (teamPlan === "free" || teamPlan === "starter") {
112115
bottomSection = (
113116
<div className="lg:px6 flex items-center justify-between gap-4 border-border border-t px-4 py-4">
114117
<p className="text-muted-foreground text-sm">
@@ -127,7 +130,7 @@ export function InviteSection(props: {
127130
) : (
128131
<Button variant="outline" size="sm" asChild>
129132
<Link
130-
href={`/team/${props.team.slug}/~/settings/billing`}
133+
href={`/team/${props.team.slug}/~/settings/billing?showPlans=true&highlight=growth`}
131134
className="gap-2"
132135
>
133136
Upgrade
@@ -147,29 +150,16 @@ export function InviteSection(props: {
147150
} else {
148151
bottomSection = (
149152
<div className="flex items-center border-border border-t px-4 py-4 lg:justify-between lg:px-6">
150-
{teamPlan === "pro" ? (
151-
<p className="text-muted-foreground text-sm">
152-
Team members are billed according to your plan.{" "}
153-
<Link
154-
href="https://meetings.hubspot.com/sales-thirdweb/thirdweb-pro"
155-
target="_blank"
156-
className="text-link-foreground hover:text-foreground"
157-
>
158-
Reach out to sales <ExternalLinkIcon className="inline size-3" />.
159-
</Link>
160-
</p>
161-
) : (
162-
<p className="text-muted-foreground text-sm">
163-
Team members are billed according to your plan.{" "}
164-
<Link
165-
href="https://thirdweb.com/pricing"
166-
target="_blank"
167-
className="text-link-foreground hover:text-foreground"
168-
>
169-
View pricing <ExternalLinkIcon className="inline size-3" />
170-
</Link>
171-
</p>
172-
)}
153+
<p className="text-muted-foreground text-sm">
154+
Team members are billed according to your plan.{" "}
155+
<Link
156+
href="https://thirdweb.com/pricing"
157+
target="_blank"
158+
className="text-link-foreground hover:text-foreground"
159+
>
160+
View pricing <ExternalLinkIcon className="inline size-3" />
161+
</Link>
162+
</p>
173163

174164
<div className="flex gap-3">
175165
{!props.shouldHideInviteButton && (

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/dedicated/(general)/_components.tsx

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import type { Team } from "@/api/team";
2-
import { UnderlineLink } from "@/components/ui/UnderlineLink";
32
import { Button } from "@/components/ui/button";
4-
import { Separator } from "@/components/ui/separator";
53
import { TrackedLinkTW } from "@/components/ui/tracked-link";
6-
import { PRO_CONTACT_US_URL } from "constants/pro";
74
import { ArrowRightIcon, DownloadIcon, ExternalLinkIcon } from "lucide-react";
85
import Link from "next/link";
96

@@ -77,91 +74,13 @@ function EngineInfoSection(props: { team_slug: string; project_slug: string }) {
7774
);
7875
}
7976

80-
function CloudHostedEngineSection(props: {
81-
teamPlan: Exclude<Team["billingPlan"], "accelerate" | "scale">;
82-
teamSlug: string;
83-
}) {
84-
return (
85-
<div className="flex flex-col">
86-
<h3 className="mb-0.5 font-semibold text-lg tracking-tight">
87-
Get Managed Engine
88-
</h3>
89-
90-
{props.teamPlan !== "pro" ? (
91-
<div>
92-
<p className="text-muted-foreground text-sm">
93-
Upgrade your plan to{" "}
94-
<UnderlineLink href="/pricing" target="_blank">
95-
Accelerate
96-
</UnderlineLink>{" "}
97-
or{" "}
98-
<UnderlineLink href="/pricing" target="_blank">
99-
Scale
100-
</UnderlineLink>{" "}
101-
to get a managed Engine instance
102-
</p>
103-
104-
<div className="h-5" />
105-
<div className="flex justify-start gap-3">
106-
<Button
107-
variant="outline"
108-
size="sm"
109-
asChild
110-
className="gap-2 bg-card"
111-
>
112-
<Link href={`/team/${props.teamSlug}/~/settings/billing`}>
113-
Upgrade Plan
114-
<ArrowRightIcon className="size-3 text-muted-foreground" />
115-
</Link>
116-
</Button>
117-
118-
<Button
119-
variant="outline"
120-
size="sm"
121-
asChild
122-
className="gap-2 bg-card"
123-
>
124-
<Link href="/pricing" target="_blank">
125-
View Pricing
126-
<ExternalLinkIcon className="size-3 text-muted-foreground" />
127-
</Link>
128-
</Button>
129-
</div>
130-
</div>
131-
) : (
132-
<div>
133-
<p className="mb-4 text-muted-foreground text-sm">
134-
Contact us to get a managed engine for your team
135-
</p>
136-
<Button variant="outline" size="sm" asChild className="gap-2">
137-
<Link href={PRO_CONTACT_US_URL} target="_blank">
138-
Contact Us
139-
<ExternalLinkIcon className="size-3 text-muted-foreground" />
140-
</Link>
141-
</Button>
142-
</div>
143-
)}
144-
</div>
145-
);
146-
}
147-
14877
export function EngineFooterCard(props: {
14978
teamPlan: Team["billingPlan"];
15079
team_slug: string;
15180
project_slug: string;
15281
}) {
15382
return (
15483
<div className="relative rounded-lg border p-6">
155-
{props.teamPlan === "accelerate" || props.teamPlan === "scale" ? null : (
156-
<>
157-
<CloudHostedEngineSection
158-
teamPlan={props.teamPlan}
159-
teamSlug={props.team_slug}
160-
/>
161-
<Separator className="my-5" />
162-
</>
163-
)}
164-
16584
<EngineInfoSection
16685
team_slug={props.team_slug}
16786
project_slug={props.project_slug}

apps/dashboard/src/components/embedded-wallets/Configure/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export const InAppWalletSettingsUI: React.FC<
181181
const authRequiredPlan = "growth";
182182
const brandingRequiredPlan = "starter";
183183

184-
// accelerate or higher plan required
184+
// growth or higher plan required
185185
const canEditSmsCountries =
186186
planToTierRecordForGating[props.teamPlan] >=
187187
planToTierRecordForGating[authRequiredPlan];

apps/dashboard/src/components/settings/Account/Billing/GatedSwitch.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function Variants() {
5757
<SelectContent>
5858
<SelectItem value="free">Free</SelectItem>
5959
<SelectItem value="growth">Growth</SelectItem>
60-
<SelectItem value="accelerate">Accelerate</SelectItem>
60+
<SelectItem value="scale">Scale</SelectItem>
6161
<SelectItem value="pro">Pro</SelectItem>
6262
</SelectContent>
6363
</Select>

0 commit comments

Comments
 (0)