Skip to content

Commit d484ae0

Browse files
committed
[TOOL-4471] GatedSwitch updates, Make In-app wallet branding gated by starter+
1 parent a3a4bf4 commit d484ae0

File tree

10 files changed

+103
-32
lines changed

10 files changed

+103
-32
lines changed

apps/dashboard/src/@/components/ui/tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ export function ToolTipLabel(props: {
5454
align={props.align}
5555
sideOffset={10}
5656
className={cn(
57-
"max-w-[400px] whitespace-normal leading-relaxed",
57+
"max-w-[400px] whitespace-normal p-0 leading-relaxed",
5858
props.contentClassName,
5959
)}
6060
>
61-
<div className="flex items-center gap-1.5 p-2 text-sm">
61+
<div className="flex items-center gap-1.5 p-4 text-sm">
6262
{props.leftIcon}
6363
{props.label}
6464
{props.rightIcon}

apps/dashboard/src/app/(app)/components/TeamPlanBadge.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const teamPlanToBadgeVariant: Record<
88
> = {
99
// gray
1010
free: "secondary",
11-
starter: "secondary",
1211
// yellow
12+
starter: "warning",
1313
starter_legacy: "warning",
1414
growth_legacy: "warning",
1515
// green
@@ -20,7 +20,7 @@ const teamPlanToBadgeVariant: Record<
2020
pro: "default",
2121
};
2222

23-
function getTeamPlanBadgeLabel(plan: Team["billingPlan"]) {
23+
export function getTeamPlanBadgeLabel(plan: Team["billingPlan"]) {
2424
if (plan === "growth_legacy") {
2525
return "Growth - Legacy";
2626
}
@@ -33,13 +33,14 @@ function getTeamPlanBadgeLabel(plan: Team["billingPlan"]) {
3333
export function TeamPlanBadge(props: {
3434
plan: Team["billingPlan"];
3535
className?: string;
36+
postfix?: string;
3637
}) {
3738
return (
3839
<Badge
3940
variant={teamPlanToBadgeVariant[props.plan]}
4041
className={cn("px-1.5 capitalize", props.className)}
4142
>
42-
{getTeamPlanBadgeLabel(props.plan)}
43+
{`${getTeamPlanBadgeLabel(props.plan)}${props.postfix || ""}`}
4344
</Badge>
4445
);
4546
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.client.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import { PlanInfoCardUI } from "./PlanInfoCard";
77
export function PlanInfoCardClient(props: {
88
subscriptions: TeamSubscription[];
99
team: Team;
10+
openPlanSheetButtonByDefault: boolean;
11+
highlightPlan: Team["billingPlan"] | undefined;
1012
}) {
1113
return (
1214
<PlanInfoCardUI
15+
openPlanSheetButtonByDefault={props.openPlanSheetButtonByDefault}
1316
team={props.team}
1417
subscriptions={props.subscriptions}
1518
getTeam={async () => {
@@ -26,6 +29,7 @@ export function PlanInfoCardClient(props: {
2629

2730
return res.data.result;
2831
}}
32+
highlightPlan={props.highlightPlan}
2933
/>
3034
);
3135
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.stories.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ function Story(props: {
118118
team={team}
119119
subscriptions={zeroUsageOnDemandSubs}
120120
getTeam={teamTeamStub}
121+
highlightPlan={undefined}
122+
openPlanSheetButtonByDefault={false}
121123
/>
122124
</BadgeContainer>
123125

@@ -129,6 +131,8 @@ function Story(props: {
129131
}}
130132
subscriptions={zeroUsageOnDemandSubs}
131133
getTeam={teamTeamStub}
134+
highlightPlan={undefined}
135+
openPlanSheetButtonByDefault={false}
132136
/>
133137
</BadgeContainer>
134138

@@ -137,6 +141,8 @@ function Story(props: {
137141
team={team}
138142
subscriptions={trialPlanZeroUsageOnDemandSubs}
139143
getTeam={teamTeamStub}
144+
highlightPlan={undefined}
145+
openPlanSheetButtonByDefault={false}
140146
/>
141147
</BadgeContainer>
142148

@@ -145,6 +151,8 @@ function Story(props: {
145151
team={team}
146152
subscriptions={subsWith1Usage}
147153
getTeam={teamTeamStub}
154+
highlightPlan={undefined}
155+
openPlanSheetButtonByDefault={false}
148156
/>
149157
</BadgeContainer>
150158

@@ -153,6 +161,8 @@ function Story(props: {
153161
team={team}
154162
subscriptions={subsWith4Usage}
155163
getTeam={teamTeamStub}
164+
highlightPlan={undefined}
165+
openPlanSheetButtonByDefault={false}
156166
/>
157167
</BadgeContainer>
158168
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/PlanInfoCard.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ export function PlanInfoCardUI(props: {
2828
subscriptions: TeamSubscription[];
2929
team: Team;
3030
getTeam: () => Promise<Team>;
31+
openPlanSheetButtonByDefault: boolean;
32+
highlightPlan: Team["billingPlan"] | undefined;
3133
}) {
32-
const { subscriptions, team } = props;
34+
const { subscriptions, team, openPlanSheetButtonByDefault } = props;
3335
const validPlan = getValidTeamPlan(team);
3436
const isActualFreePlan = team.billingPlan === "free";
35-
const [isPlanSheetOpen, setIsPlanSheetOpen] = useState(false);
37+
const [isPlanSheetOpen, setIsPlanSheetOpen] = useState(
38+
openPlanSheetButtonByDefault,
39+
);
3640

3741
const planSub = subscriptions.find(
3842
(subscription) => subscription.type === "PLAN",
@@ -54,6 +58,7 @@ export function PlanInfoCardUI(props: {
5458
isOpen={isPlanSheetOpen}
5559
onOpenChange={setIsPlanSheetOpen}
5660
getTeam={props.getTeam}
61+
highlightPlan={props.highlightPlan}
5762
/>
5863

5964
<div className="flex flex-col gap-4 p-4 lg:flex-row lg:items-center lg:justify-between lg:p-6">
@@ -298,6 +303,7 @@ function ViewPlansSheet(props: {
298303
isOpen: boolean;
299304
onOpenChange: (open: boolean) => void;
300305
getTeam: () => Promise<Team>;
306+
highlightPlan: Team["billingPlan"] | undefined;
301307
}) {
302308
return (
303309
<Sheet open={props.isOpen} onOpenChange={props.onOpenChange}>
@@ -309,6 +315,7 @@ function ViewPlansSheet(props: {
309315
team={props.team}
310316
trialPeriodEndedAt={props.trialPeriodEndedAt}
311317
getTeam={props.getTeam}
318+
highlightPlan={props.highlightPlan}
312319
/>
313320
</SheetContent>
314321
</Sheet>

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getTeamBySlug } from "@/api/team";
1+
import { type Team, getTeamBySlug } from "@/api/team";
22
import { getTeamSubscriptions } from "@/api/team-subscription";
33
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
44
import { Billing } from "components/settings/Account/Billing";
@@ -10,8 +10,13 @@ export default async function Page(props: {
1010
params: Promise<{
1111
team_slug: string;
1212
}>;
13+
searchParams: Promise<{
14+
showPlans?: string;
15+
highlight?: string;
16+
}>;
1317
}) {
1418
const params = await props.params;
19+
const searchParams = await props.searchParams;
1520
const pagePath = `/team/${params.team_slug}/settings/billing`;
1621

1722
const [account, team, authToken] = await Promise.all([
@@ -41,6 +46,8 @@ export default async function Page(props: {
4146

4247
return (
4348
<Billing
49+
highlightPlan={searchParams.highlight as Team["billingPlan"] | undefined}
50+
openPlanSheetButtonByDefault={searchParams.showPlans === "true"}
4451
team={team}
4552
subscriptions={subscriptions}
4653
twAccount={account}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export const InAppWalletSettingsUI: React.FC<
179179
!!config.applicationImageUrl?.length || !!config.applicationName?.length;
180180

181181
const authRequiredPlan = "accelerate";
182+
const brandingRequiredPlan = "starter";
182183

183184
// accelerate or higher plan required
184185
const canEditSmsCountries =
@@ -275,7 +276,7 @@ export const InAppWalletSettingsUI: React.FC<
275276
form={form}
276277
teamPlan={props.teamPlan}
277278
teamSlug={props.teamSlug}
278-
requiredPlan={authRequiredPlan}
279+
requiredPlan={brandingRequiredPlan}
279280
/>
280281

281282
<NativeAppsFieldset form={form} />

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

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import type { Team } from "@/api/team";
2+
import { Button } from "@/components/ui/button";
23
import { Switch } from "@/components/ui/switch";
34
import { ToolTipLabel } from "@/components/ui/tooltip";
45
import { TrackedLinkTW } from "@/components/ui/tracked-link";
56
import { cn } from "@/lib/utils";
6-
import { TeamPlanBadge } from "../../../../app/(app)/components/TeamPlanBadge";
7+
import { ExternalLinkIcon } from "lucide-react";
8+
import {
9+
TeamPlanBadge,
10+
getTeamPlanBadgeLabel,
11+
} from "../../../../app/(app)/components/TeamPlanBadge";
712
import { planToTierRecordForGating } from "./planToTierRecord";
813

914
type SwitchProps = React.ComponentProps<typeof Switch>;
@@ -26,26 +31,40 @@ export const GatedSwitch: React.FC<GatedSwitchProps> = (
2631
return (
2732
<ToolTipLabel
2833
hoverable
29-
contentClassName="max-w-[300px]"
3034
label={
3135
isUpgradeRequired ? (
32-
<span>
33-
To access this feature, <br /> Upgrade to the{" "}
34-
<TrackedLinkTW
35-
target="_blank"
36-
href={`/team/${props.teamSlug}/~/settings/billing`}
37-
category="advancedFeature"
38-
label={props.trackingLabel}
39-
className="text-link-foreground capitalize hover:text-foreground"
40-
>
41-
{props.requiredPlan} plan
42-
</TrackedLinkTW>
43-
</span>
36+
<div className="w-full min-w-[280px]">
37+
<h3 className="font-medium text-base">
38+
<span className="capitalize">
39+
{getTeamPlanBadgeLabel(props.requiredPlan)}+
40+
</span>{" "}
41+
plan required
42+
</h3>
43+
<p className="mb-3.5 text-muted-foreground">
44+
Upgrade your plan to use this feature
45+
</p>
46+
47+
<div className="flex w-full flex-col gap-2">
48+
<Button asChild size="sm" className="justify-start gap-2">
49+
<TrackedLinkTW
50+
target="_blank"
51+
href={`/team/${props.teamSlug}/~/settings/billing?showPlans=true&highlight=${props.requiredPlan}`}
52+
category="advancedFeature"
53+
label="checkout"
54+
>
55+
{`Upgrade to ${getTeamPlanBadgeLabel(props.requiredPlan)} plan`}
56+
<ExternalLinkIcon className="size-4" />
57+
</TrackedLinkTW>
58+
</Button>
59+
</div>
60+
</div>
4461
) : undefined
4562
}
4663
>
4764
<div className="inline-flex items-center gap-2">
48-
{isUpgradeRequired && <TeamPlanBadge plan={props.requiredPlan} />}
65+
{isUpgradeRequired && (
66+
<TeamPlanBadge plan={props.requiredPlan} postfix="+" />
67+
)}
4968
<Switch
5069
{...props.switchProps}
5170
checked={props.switchProps?.checked && !isUpgradeRequired}

apps/dashboard/src/components/settings/Account/Billing/Pricing.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ interface BillingPricingProps {
2929
team: Team;
3030
trialPeriodEndedAt: string | undefined;
3131
getTeam: () => Promise<Team>;
32+
highlightPlan: Team["billingPlan"] | undefined;
3233
}
3334

3435
type CtaLink =
@@ -49,6 +50,7 @@ export const BillingPricing: React.FC<BillingPricingProps> = ({
4950
team,
5051
trialPeriodEndedAt,
5152
getTeam,
53+
highlightPlan,
5254
}) => {
5355
const validTeamPlan = getValidTeamPlan(team);
5456
const [isPending, startTransition] = useTransition();
@@ -65,17 +67,28 @@ export const BillingPricing: React.FC<BillingPricingProps> = ({
6567
const isCurrentPlanScheduledToCancel = team.planCancellationDate !== null;
6668

6769
const highlightGrowthPlan =
68-
!isCurrentPlanScheduledToCancel &&
69-
(validTeamPlan === "free" ||
70-
validTeamPlan === "starter" ||
71-
validTeamPlan === "growth_legacy");
70+
highlightPlan === "growth" ||
71+
(!highlightPlan &&
72+
!isCurrentPlanScheduledToCancel &&
73+
(validTeamPlan === "free" ||
74+
validTeamPlan === "starter" ||
75+
validTeamPlan === "growth_legacy"));
7276

7377
const highlightStarterPlan =
74-
!isCurrentPlanScheduledToCancel && validTeamPlan === "starter_legacy";
78+
highlightPlan === "starter" ||
79+
(!highlightPlan &&
80+
!isCurrentPlanScheduledToCancel &&
81+
validTeamPlan === "starter_legacy");
7582
const highlightAcceleratePlan =
76-
!isCurrentPlanScheduledToCancel && validTeamPlan === "growth";
83+
highlightPlan === "accelerate" ||
84+
(!highlightPlan &&
85+
!isCurrentPlanScheduledToCancel &&
86+
validTeamPlan === "growth");
7787
const highlightScalePlan =
78-
!isCurrentPlanScheduledToCancel && validTeamPlan === "accelerate";
88+
highlightPlan === "scale" ||
89+
(!highlightPlan &&
90+
!isCurrentPlanScheduledToCancel &&
91+
validTeamPlan === "accelerate");
7992

8093
return (
8194
<div>

apps/dashboard/src/components/settings/Account/Billing/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,30 @@ interface BillingProps {
1313
subscriptions: TeamSubscription[];
1414
twAccount: Account;
1515
client: ThirdwebClient;
16+
openPlanSheetButtonByDefault: boolean;
17+
highlightPlan: Team["billingPlan"] | undefined;
1618
}
1719

1820
export const Billing: React.FC<BillingProps> = ({
1921
team,
2022
subscriptions,
2123
twAccount,
2224
client,
25+
openPlanSheetButtonByDefault,
26+
highlightPlan,
2327
}) => {
2428
const validPayment =
2529
team.billingStatus === "validPayment" || team.billingStatus === "pastDue";
2630

2731
return (
2832
<div className="flex flex-col gap-12">
2933
<div>
30-
<PlanInfoCardClient team={team} subscriptions={subscriptions} />
34+
<PlanInfoCardClient
35+
team={team}
36+
subscriptions={subscriptions}
37+
openPlanSheetButtonByDefault={openPlanSheetButtonByDefault}
38+
highlightPlan={highlightPlan}
39+
/>
3140
</div>
3241

3342
<CreditsInfoCard

0 commit comments

Comments
 (0)