Skip to content

Commit 8226c74

Browse files
committed
Dashboard, Portal, Playground: Add Bridge Product
1 parent 29be72e commit 8226c74

File tree

15 files changed

+334
-117
lines changed

15 files changed

+334
-117
lines changed

apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
132132
{props.title}
133133
</h2>
134134
{/* description */}
135-
<p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2">
135+
<p className="text-sm text-muted-foreground text-pretty">
136136
{props.description}
137137
</p>
138138
</div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { BringToFrontIcon as BridgeIcon } from "lucide-react";
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use client";
2+
3+
import { BadgeDollarSignIcon, CodeIcon } from "lucide-react";
4+
import { FeatureCard } from "../payments/components/FeatureCard.client";
5+
6+
export function QuickStartSection(props: {
7+
teamSlug: string;
8+
projectSlug: string;
9+
clientId: string;
10+
teamId: string;
11+
}) {
12+
return (
13+
<section>
14+
<div className="mb-4">
15+
<h2 className="font-semibold text-xl tracking-tight">Quick Start</h2>
16+
<p className="text-muted-foreground text-sm">
17+
Choose how to integrate bridge into your project.
18+
</p>
19+
</div>
20+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
21+
<FeatureCard
22+
title="Earn Fees"
23+
description="Setup fees to earn any time a user swaps or bridges funds."
24+
icon={BadgeDollarSignIcon}
25+
id="fees"
26+
setupTime={1}
27+
features={[
28+
"Fees on every purchase",
29+
"Custom percentage",
30+
"Directly to your wallet",
31+
]}
32+
link={{
33+
href: `/team/${props.teamSlug}/${props.projectSlug}/settings/payments`,
34+
label: "Configure Fees",
35+
}}
36+
/>
37+
<FeatureCard
38+
title="UI Components"
39+
description="Instantly add bridging to your React app with prebuilt components."
40+
icon={CodeIcon}
41+
id="components"
42+
setupTime={2}
43+
features={[
44+
"Drop-in components",
45+
"Supports custom user data",
46+
"Transactions, products, and direct payments",
47+
]}
48+
link={{
49+
href: "https://portal.thirdweb.com/payments/products",
50+
label: "Get Started",
51+
}}
52+
/>
53+
</div>
54+
</section>
55+
);
56+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import { Button } from "@workspace/ui/components/button";
2+
import { PlusIcon } from "lucide-react";
3+
import { redirect } from "next/navigation";
4+
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
5+
import { getAuthToken } from "@/api/auth-token";
6+
import { getProject } from "@/api/project/projects";
7+
import { ProjectPage } from "@/components/blocks/project-page/project-page";
8+
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
9+
import { BridgeIcon } from "@/icons/BridgeIcon";
10+
import { loginRedirect } from "@/utils/redirects";
11+
import { AdvancedSection } from "../payments/components/AdvancedSection.client";
12+
import { PayAnalytics } from "../payments/components/PayAnalytics";
13+
import { getUniversalBridgeFiltersFromSearchParams } from "../payments/components/time";
14+
import { CreatePaymentLinkButton } from "../payments/links/components/CreatePaymentLinkButton.client";
15+
import { QuickStartSection } from "./QuickstartSection.client";
16+
17+
export default async function Page(props: {
18+
params: Promise<{
19+
team_slug: string;
20+
project_slug: string;
21+
}>;
22+
searchParams: Promise<{
23+
from?: string | undefined | string[];
24+
to?: string | undefined | string[];
25+
interval?: string | undefined | string[];
26+
}>;
27+
}) {
28+
const [params, authToken] = await Promise.all([props.params, getAuthToken()]);
29+
30+
const project = await getProject(params.team_slug, params.project_slug);
31+
32+
if (!authToken) {
33+
loginRedirect(`/team/${params.team_slug}/${params.project_slug}/payments`);
34+
}
35+
36+
if (!project) {
37+
redirect(`/team/${params.team_slug}`);
38+
}
39+
40+
const searchParams = await props.searchParams;
41+
42+
const { range, interval } = getUniversalBridgeFiltersFromSearchParams({
43+
defaultRange: "last-30",
44+
from: searchParams.from,
45+
interval: searchParams.interval,
46+
to: searchParams.to,
47+
});
48+
49+
const client = getClientThirdwebClient({
50+
jwt: authToken,
51+
teamId: project.teamId,
52+
});
53+
54+
return (
55+
<ProjectPage
56+
header={{
57+
client,
58+
title: "Bridge",
59+
icon: BridgeIcon,
60+
description: (
61+
<>
62+
Bridge allows you to enable seamless onramps, swaps, and cross-chain
63+
transfers <br className="max-sm:hidden" /> to move funds
64+
effortlessly across any EVM network
65+
</>
66+
),
67+
actions: {
68+
primary: {
69+
component: (
70+
<CreatePaymentLinkButton
71+
clientId={project.publishableKey}
72+
teamId={project.teamId}
73+
>
74+
<Button className="gap-1.5 rounded-full" size="sm">
75+
<PlusIcon className="size-4" />
76+
Create Payment
77+
</Button>
78+
</CreatePaymentLinkButton>
79+
),
80+
},
81+
},
82+
settings: {
83+
href: `/team/${params.team_slug}/${params.project_slug}/settings/payments`,
84+
},
85+
links: [
86+
{
87+
type: "docs",
88+
href: "https://portal.thirdweb.com/payments",
89+
},
90+
{
91+
type: "playground",
92+
href: "https://playground.thirdweb.com/payments/ui-components",
93+
},
94+
{
95+
type: "api",
96+
href: "https://api.thirdweb.com/reference#tag/payments",
97+
},
98+
{
99+
type: "webhooks",
100+
href: `/team/${params.team_slug}/${params.project_slug}/webhooks/payments`,
101+
},
102+
],
103+
}}
104+
footer={{
105+
center: {
106+
links: [
107+
{
108+
href: "https://playground.thirdweb.com/payments/ui-components",
109+
label: "UI Component",
110+
},
111+
{
112+
href: "https://playground.thirdweb.com/connect/payments/fund-wallet",
113+
label: "Buy Crypto",
114+
},
115+
{
116+
href: "https://playground.thirdweb.com/connect/payments/commerce",
117+
label: "Checkout",
118+
},
119+
{
120+
href: "https://playground.thirdweb.com/connect/payments/transactions",
121+
label: "Transactions",
122+
},
123+
],
124+
title: "Demos",
125+
},
126+
left: {
127+
links: [
128+
{
129+
href: "https://portal.thirdweb.com/payments",
130+
label: "Overview",
131+
},
132+
{
133+
href: "https://portal.thirdweb.com/typescript/v5/convertCryptoToFiat",
134+
label: "TypeScript",
135+
},
136+
{
137+
href: "https://portal.thirdweb.com/react/v5/pay/fund-wallets",
138+
label: "React",
139+
},
140+
{
141+
href: "https://portal.thirdweb.com/dotnet/universal-bridge/quickstart",
142+
label: ".NET",
143+
},
144+
],
145+
title: "Documentation",
146+
},
147+
right: {
148+
links: [
149+
{
150+
href: "https://www.youtube.com/watch?v=aBu175-VsNY",
151+
label: "Implement cross-chain payments in any app",
152+
},
153+
],
154+
title: "Tutorials",
155+
},
156+
}}
157+
>
158+
<div className="flex flex-col gap-12">
159+
<ResponsiveSearchParamsProvider value={searchParams}>
160+
<PayAnalytics
161+
client={client}
162+
interval={interval}
163+
projectClientId={project.publishableKey}
164+
projectId={project.id}
165+
range={range}
166+
teamId={project.teamId}
167+
authToken={authToken}
168+
/>
169+
</ResponsiveSearchParamsProvider>
170+
171+
<QuickStartSection
172+
projectSlug={params.project_slug}
173+
teamSlug={params.team_slug}
174+
clientId={project.publishableKey}
175+
teamId={project.teamId}
176+
/>
177+
178+
<AdvancedSection
179+
projectSlug={params.project_slug}
180+
teamSlug={params.team_slug}
181+
/>
182+
</div>
183+
</ProjectPage>
184+
);
185+
}

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from "lucide-react";
1313
import { FullWidthSidebarLayout } from "@/components/blocks/full-width-sidebar-layout";
1414
import { Badge } from "@/components/ui/badge";
15+
import { BridgeIcon } from "@/icons/BridgeIcon";
1516
import { ContractIcon } from "@/icons/ContractIcon";
1617
import { InsightIcon } from "@/icons/InsightIcon";
1718
import { NebulaIcon } from "@/icons/NebulaIcon";
@@ -72,6 +73,11 @@ export function ProjectSidebarLayout(props: {
7273
icon: PayIcon,
7374
label: "Payments",
7475
},
76+
{
77+
href: `${props.layoutPath}/bridge`,
78+
icon: BridgeIcon,
79+
label: "Bridge",
80+
},
7581
{
7682
href: `${props.layoutPath}/tokens`,
7783
icon: TokenIcon,

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/AdvancedSection.client.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export function AdvancedSection({
2424
description="Build your own branded experiences with the HTTP API or TypeScript SDK."
2525
icon={CodeIcon}
2626
setupTime={10}
27-
color="green"
2827
id="http_api"
2928
features={["Route discovery", "Real-time token prices"]}
3029
link={{
@@ -37,7 +36,6 @@ export function AdvancedSection({
3736
description="Create Webhooks to get notified on each purchase or transaction."
3837
icon={WebhookIcon}
3938
setupTime={5}
40-
color="green"
4139
id="webhooks"
4240
features={["Instant events", "Transaction verification"]}
4341
link={{
@@ -49,14 +47,12 @@ export function AdvancedSection({
4947
title="Swap Tokens"
5048
icon={ArrowRightLeftIcon}
5149
setupTime={5}
52-
color="green"
5350
id="swap_tokens"
5451
features={["Swap any token", "Cross-chain swaps"]}
5552
description="Swap tokens cross-chain with dedicated swapping endpoints."
5653
link={{
5754
href: `https://portal.thirdweb.com/payments/swap`,
5855
label: "Setup Swaps",
59-
target: "_blank",
6056
}}
6157
/>
6258
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/EmptyState.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1+
import { XIcon } from "lucide-react";
12
import { Card } from "@/components/ui/card";
23

34
export function EmptyState(props: {
4-
icon: React.FC<{ className?: string }>;
55
title: string;
66
description: string;
77
buttons: Array<React.ReactNode>;
88
}) {
99
return (
10-
<Card className="flex flex-col p-16 gap-8 items-center justify-center">
11-
<div className="bg-violet-800/25 text-muted-foreground rounded-full size-16 flex items-center justify-center">
12-
<props.icon className="size-8 text-violet-500" />
10+
<Card className="flex flex-col p-16 gap-5 items-center justify-center">
11+
<div className="rounded-full p-2.5 border bg-background">
12+
<XIcon className="size-5 text-muted-foreground" />
1313
</div>
1414
<div className="flex flex-col gap-1 items-center text-center">
15-
<h3 className="text-foreground font-medium text-xl">{props.title}</h3>
15+
<h3 className="text-foreground font-medium text-lg tracking-tight">
16+
{props.title}
17+
</h3>
1618
<p className="text-muted-foreground text-sm max-w-md">
1719
{props.description}
1820
</p>

0 commit comments

Comments
 (0)