Skip to content

Commit b4d1132

Browse files
committed
[TOOL-4858] Update Team Usage Overview page UI for free plan
1 parent 66270f7 commit b4d1132

File tree

3 files changed

+123
-91
lines changed

3 files changed

+123
-91
lines changed

apps/dashboard/src/@/components/blocks/upsell-wrapper.tsx

Lines changed: 99 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function UpsellWrapper({
4747
}
4848

4949
return (
50-
<div className={cn("relative flex-1", className)}>
50+
<div className={cn("relative flex-1 flex flex-col", className)}>
5151
{/* Background content - blurred and non-interactive */}
5252
<div className="absolute inset-0 overflow-hidden">
5353
<div className="pointer-events-none select-none opacity-60 blur-[1px]">
@@ -59,84 +59,109 @@ export function UpsellWrapper({
5959
<div className="absolute inset-0 bg-gradient-to-b from-muted/20 via-muted/30 to-background" />
6060

6161
{/* Upsell content */}
62-
<div className="relative z-10 flex items-center justify-center p-16">
63-
<Card className="w-full max-w-2xl border-2 shadow-2xl">
64-
<CardHeader className="space-y-4 text-center">
65-
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full border-2 bg-muted">
66-
<LockIcon className="h-8 w-8 text-muted-foreground" />
67-
</div>
62+
<div className="relative z-10 flex items-center justify-center grow py-20 px-6">
63+
<UpsellContent
64+
benefits={benefits}
65+
currentPlan={currentPlan}
66+
featureDescription={featureDescription}
67+
featureName={featureName}
68+
requiredPlan={requiredPlan}
69+
teamSlug={teamSlug}
70+
/>
71+
</div>
72+
</div>
73+
);
74+
}
6875

69-
<div className="space-y-2">
70-
<TeamPlanBadge
71-
plan="scale"
72-
postfix=" Feature"
73-
teamSlug={teamSlug}
74-
/>
75-
<CardTitle className="font-bold text-2xl text-foreground md:text-3xl">
76-
Unlock {featureName}
77-
</CardTitle>
78-
<CardDescription className="mx-auto max-w-md text-base text-muted-foreground">
79-
{featureDescription}
80-
</CardDescription>
81-
</div>
82-
</CardHeader>
76+
export function UpsellContent(props: {
77+
teamSlug: string;
78+
featureName: string;
79+
featureDescription: string;
80+
requiredPlan: Team["billingPlan"];
81+
currentPlan: Team["billingPlan"];
82+
benefits?: {
83+
description: string;
84+
status: "available" | "soon";
85+
}[];
86+
}) {
87+
return (
88+
<Card className="w-full max-w-xl border shadow-2xl">
89+
<CardHeader className="space-y-4 text-center">
90+
<div className="mx-auto flex p-4 items-center justify-center rounded-full border bg-card">
91+
<LockIcon className="size-8 text-muted-foreground" />
92+
</div>
8393

84-
<CardContent className="space-y-6">
85-
{benefits.length > 0 && (
86-
<div className="space-y-3">
87-
<h4 className="font-semibold text-muted-foreground text-sm uppercase tracking-wide">
88-
What you'll get:
89-
</h4>
90-
<div className="grid gap-2">
91-
{benefits.map((benefit) => (
92-
<div
93-
className="flex items-center gap-3"
94-
key={benefit.description}
95-
>
96-
<div className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-accent">
97-
<SparklesIcon className="h-3 w-3 text-success-text" />
98-
</div>
99-
<span className="text-sm">{benefit.description}</span>
100-
{benefit.status === "soon" && (
101-
<Badge className="text-xs" variant="secondary">
102-
Coming Soon
103-
</Badge>
104-
)}
105-
</div>
106-
))}
107-
</div>
108-
</div>
109-
)}
94+
<div className="space-y-4">
95+
<TeamPlanBadge
96+
plan={props.requiredPlan}
97+
postfix=" Feature"
98+
teamSlug={props.teamSlug}
99+
/>
100+
<div className="space-y-1">
101+
<CardTitle className="font-bold text-2xl text-foreground md:text-3xl">
102+
Unlock {props.featureName}
103+
</CardTitle>
104+
<CardDescription className="mx-auto max-w-md text-base text-muted-foreground">
105+
{props.featureDescription}
106+
</CardDescription>
107+
</div>
108+
</div>
109+
</CardHeader>
110110

111-
<div className="flex flex-col gap-3 pt-4 sm:flex-row">
112-
<Button asChild className="flex-1 py-3 font-semibold" size="lg">
113-
<Link
114-
href={`/team/${teamSlug}/~/settings/billing?showPlans=true&highlight=${requiredPlan}`}
115-
>
116-
<CrownIcon className="mr-2 h-4 w-4" />
117-
Upgrade to{" "}
118-
<span className="ml-1 capitalize">{requiredPlan}</span>
119-
</Link>
120-
</Button>
121-
<Button asChild className="md:flex-1" size="lg" variant="outline">
122-
<Link
123-
href={`/team/${teamSlug}/~/settings/billing?showPlans=true`}
111+
<CardContent className="space-y-6">
112+
{props.benefits && props.benefits.length > 0 && (
113+
<div className="space-y-3">
114+
<h4 className="font-semibold text-muted-foreground text-sm uppercase tracking-wide">
115+
What you'll get:
116+
</h4>
117+
<div className="grid gap-2">
118+
{props.benefits.map((benefit) => (
119+
<div
120+
className="flex items-center gap-3"
121+
key={benefit.description}
124122
>
125-
View All Plans
126-
</Link>
127-
</Button>
123+
<div className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-accent">
124+
<SparklesIcon className="h-3 w-3 text-success-text" />
125+
</div>
126+
<span className="text-sm">{benefit.description}</span>
127+
{benefit.status === "soon" && (
128+
<Badge className="text-xs" variant="secondary">
129+
Coming Soon
130+
</Badge>
131+
)}
132+
</div>
133+
))}
128134
</div>
135+
</div>
136+
)}
129137

130-
<div className="pt-2 text-center">
131-
<p className="text-muted-foreground text-xs">
132-
You are currently on the{" "}
133-
<span className="font-medium capitalize">{currentPlan}</span>{" "}
134-
plan.
135-
</p>
136-
</div>
137-
</CardContent>
138-
</Card>
139-
</div>
140-
</div>
138+
<div className="flex flex-col gap-3 pt-4 sm:flex-row">
139+
<Button asChild className="flex-1 py-3 font-semibold" size="lg">
140+
<Link
141+
href={`/team/${props.teamSlug}/~/settings/billing?showPlans=true&highlight=${props.requiredPlan}`}
142+
>
143+
<CrownIcon className="mr-2 h-4 w-4" />
144+
Upgrade to{" "}
145+
<span className="ml-1 capitalize">{props.requiredPlan}</span>
146+
</Link>
147+
</Button>
148+
<Button asChild className="md:flex-1" size="lg" variant="outline">
149+
<Link
150+
href={`/team/${props.teamSlug}/~/settings/billing?showPlans=true`}
151+
>
152+
View All Plans
153+
</Link>
154+
</Button>
155+
</div>
156+
157+
<div className="pt-2 text-center">
158+
<p className="text-muted-foreground text-xs">
159+
You are currently on the{" "}
160+
<span className="font-medium capitalize">{props.currentPlan}</span>{" "}
161+
plan.
162+
</p>
163+
</div>
164+
</CardContent>
165+
</Card>
141166
);
142167
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/audit-log/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { redirect } from "next/navigation";
2-
import { getTeamBySlug } from "../../../../../../../@/api/team";
3-
import { UpsellWrapper } from "../../../../../../../@/components/blocks/upsell-wrapper";
2+
import { getTeamBySlug } from "@/api/team";
3+
import { UpsellWrapper } from "@/components/blocks/upsell-wrapper";
44

55
export default async function Layout(props: {
66
children: React.ReactNode;

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { InfoIcon } from "lucide-react";
33
import { redirect } from "next/navigation";
44
import { getTeamBySlug } from "@/api/team";
55
import { getBilledUsage } from "@/api/usage/billing-preview";
6+
import { UpsellContent } from "@/components/blocks/upsell-wrapper";
67
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
78
import {
89
Card,
@@ -12,6 +13,7 @@ import {
1213
CardHeader,
1314
CardTitle,
1415
} from "@/components/ui/card";
16+
import { getValidTeamPlan } from "@/utils/getValidTeamPlan";
1517
import { getValidAccount } from "../../../../../account/settings/getAccount";
1618
import {
1719
formatPrice,
@@ -34,23 +36,28 @@ export default async function Page(props: {
3436
}
3537

3638
const usagePreview = await getBilledUsage(team.slug);
39+
const validPlan = getValidTeamPlan(team);
40+
41+
if (validPlan === "free") {
42+
return (
43+
<div className="grow flex flex-col justify-center items-center">
44+
<UpsellContent
45+
currentPlan={team.billingPlan}
46+
featureDescription="View RPC, Wallet, Storage, Account Abstraction, Engine Cloud, Webhooks usage and more"
47+
featureName="Usage"
48+
requiredPlan="starter"
49+
teamSlug={params.team_slug}
50+
/>
51+
</div>
52+
);
53+
}
3754

3855
if (usagePreview.status === "error") {
39-
switch (usagePreview.reason) {
40-
case "free_plan":
41-
return (
42-
<div className="flex min-h-[350px] items-center justify-center rounded-lg border p-4 text-destructive-text">
43-
You are on a free plan. Please upgrade to a paid plan to view your
44-
usage.
45-
</div>
46-
);
47-
default:
48-
return (
49-
<div className="flex min-h-[350px] items-center justify-center rounded-lg border p-4 text-destructive-text">
50-
Something went wrong. Please try again later.
51-
</div>
52-
);
53-
}
56+
return (
57+
<div className="flex min-h-[350px] items-center justify-center rounded-lg border p-4 text-destructive-text">
58+
Something went wrong. Please try again later.
59+
</div>
60+
);
5461
}
5562

5663
const grandTotalCents = usagePreview.data.result.reduce((total, category) => {

0 commit comments

Comments
 (0)