Skip to content

Commit 2fd101e

Browse files
committed
Dashboard, Portal, Playground: Add Bridge Product
1 parent 928704a commit 2fd101e

File tree

39 files changed

+885
-722
lines changed

39 files changed

+885
-722
lines changed

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,15 @@ type Action =
2323
component: React.ReactNode;
2424
};
2525

26-
function Action(props: { action: Action; variant?: "default" | "secondary" }) {
26+
function Action(props: { action: Action; variant?: "default" | "outline" }) {
2727
const action = props.action;
2828

2929
return "component" in action ? (
3030
action.component
3131
) : (
3232
<Button
3333
asChild
34-
className={cn(
35-
"rounded-full",
36-
props.variant === "secondary" && "border border-border",
37-
)}
34+
className={cn("rounded-full")}
3835
size="sm"
3936
variant={props.variant}
4037
>
@@ -60,7 +57,7 @@ export type ProjectPageHeaderProps = {
6057
icon: React.FC<{ className?: string }>;
6158
isProjectIcon?: boolean;
6259
actions: {
63-
primary: Action;
60+
primary?: Action;
6461
secondary?: Action;
6562
} | null;
6663

@@ -114,7 +111,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
114111
{props.actions && (
115112
<div className="hidden lg:flex items-center gap-3">
116113
{props.actions.secondary && (
117-
<Action action={props.actions.secondary} variant="secondary" />
114+
<Action action={props.actions.secondary} variant="outline" />
118115
)}
119116

120117
{props.actions.primary && (
@@ -132,7 +129,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
132129
{props.title}
133130
</h2>
134131
{/* description */}
135-
<p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2">
132+
<p className="text-sm text-muted-foreground text-pretty">
136133
{props.description}
137134
</p>
138135
</div>
@@ -145,7 +142,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
145142
)}
146143

147144
{props.actions?.secondary && (
148-
<Action action={props.actions.secondary} variant="secondary" />
145+
<Action action={props.actions.secondary} variant="outline" />
149146
)}
150147
</div>
151148
)}
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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"use client";
2+
3+
import {
4+
ArrowRightLeftIcon,
5+
BadgeDollarSignIcon,
6+
WebhookIcon,
7+
} from "lucide-react";
8+
import { FeatureCard } from "../payments/components/FeatureCard.client";
9+
10+
export function QuickStartSection(props: {
11+
teamSlug: string;
12+
projectSlug: string;
13+
clientId: string;
14+
teamId: string;
15+
}) {
16+
return (
17+
<section>
18+
<div className="mb-4">
19+
<h2 className="font-semibold text-xl tracking-tight">Get Started</h2>
20+
<p className="text-muted-foreground text-sm">
21+
Integrate bridge into your project and enable seamless cross-chain
22+
swaps
23+
</p>
24+
</div>
25+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
26+
<FeatureCard
27+
title="Cross-chain Swap Tokens"
28+
icon={ArrowRightLeftIcon}
29+
setupTime={5}
30+
id="swap_tokens"
31+
features={["Swap any token", "Cross-chain swaps"]}
32+
description="Swap tokens cross-chain with dedicated swapping endpoints."
33+
link={{
34+
href: `https://portal.thirdweb.com/payments/swap`,
35+
label: "Setup Swaps",
36+
}}
37+
/>
38+
39+
<FeatureCard
40+
title="Earn Fees"
41+
description="Setup fees to earn any time a user swaps or bridges funds."
42+
icon={BadgeDollarSignIcon}
43+
id="fees"
44+
setupTime={1}
45+
features={[
46+
"Fees on every purchase",
47+
"Custom percentage",
48+
"Directly to your wallet",
49+
]}
50+
link={{
51+
href: `/team/${props.teamSlug}/${props.projectSlug}/settings/payments`,
52+
label: "Configure Fees",
53+
}}
54+
/>
55+
56+
<FeatureCard
57+
title="Webhooks"
58+
description="Create Webhooks to get notified on each purchase or transaction."
59+
icon={WebhookIcon}
60+
setupTime={5}
61+
id="webhooks"
62+
features={["Instant events", "Transaction verification"]}
63+
link={{
64+
href: `/team/${props.teamSlug}/${props.projectSlug}/webhooks/payments`,
65+
label: "Setup Webhooks",
66+
}}
67+
/>
68+
</div>
69+
</section>
70+
);
71+
}
Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { useForm } from "react-hook-form";
66
import { toast } from "sonner";
77
import type { ThirdwebClient } from "thirdweb";
88
import { addUniversalBridgeTokenRoute } from "@/api/universal-bridge/tokens"; // Adjust the import path
9-
import { RouteDiscoveryCard } from "@/components/blocks/RouteDiscoveryCard";
10-
import { NetworkSelectorButton } from "@/components/misc/NetworkSelectorButton";
9+
import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
1110
import {
1211
Form,
1312
FormControl,
@@ -20,6 +19,7 @@ import {
2019
type RouteDiscoveryValidationSchema,
2120
routeDiscoveryValidationSchema,
2221
} from "@/schema/validations";
22+
import { RouteDiscoveryCard } from "./RouteDiscoveryCard";
2323

2424
export const RouteDiscovery = ({
2525
project,
@@ -96,27 +96,30 @@ export const RouteDiscovery = ({
9696
>
9797
<div>
9898
<h3 className="font-semibold text-xl tracking-tight">
99-
Don't see your token listed?
99+
Add a token to Bridge
100100
</h3>
101-
<p className="mt-1.5 mb-4 text-foreground text-sm">
101+
<p className="mt-1.5 mb-4 text-muted-foreground max-w-3xl text-sm text-pretty">
102102
Select your chain and input the token address to automatically
103-
kick-off the token route discovery process. Please check back on
104-
this page within 20-40 minutes of submitting this form.
103+
kick-off the token route discovery process. <br /> This may take
104+
up to 20-40 minutes to complete.
105105
</p>
106106

107-
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
107+
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 max-w-3xl">
108108
<FormField
109109
control={form.control}
110110
name="chainId"
111111
render={({ field }) => (
112112
<FormItem>
113-
<FormLabel>Blockchain</FormLabel>
113+
<FormLabel>Chain</FormLabel>
114114
<FormControl>
115-
<NetworkSelectorButton
115+
<SingleNetworkSelector
116+
chainId={field.value}
117+
className="bg-background"
118+
disableChainId
119+
disableDeprecated
116120
client={client}
117-
onSwitchChain={(chain) => {
118-
// Update the form field value
119-
field.onChange(chain.chainId);
121+
onChange={(chain) => {
122+
field.onChange(chain, { shouldValidate: true });
120123
}}
121124
/>
122125
</FormControl>
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { WebhookIcon } from "lucide-react";
2+
import { redirect } from "next/navigation";
3+
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
4+
import { getAuthToken } from "@/api/auth-token";
5+
import { getProject } from "@/api/project/projects";
6+
import { ProjectPage } from "@/components/blocks/project-page/project-page";
7+
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
8+
import { BridgeIcon } from "@/icons/BridgeIcon";
9+
import { loginRedirect } from "@/utils/redirects";
10+
import { PayAnalytics } from "../payments/components/PayAnalytics";
11+
import { getUniversalBridgeFiltersFromSearchParams } from "../payments/components/time";
12+
import { QuickStartSection } from "./QuickstartSection.client";
13+
import { RouteDiscovery } from "./RouteDiscovery";
14+
15+
export default async function Page(props: {
16+
params: Promise<{
17+
team_slug: string;
18+
project_slug: string;
19+
}>;
20+
searchParams: Promise<{
21+
from?: string | undefined | string[];
22+
to?: string | undefined | string[];
23+
interval?: string | undefined | string[];
24+
}>;
25+
}) {
26+
const [params, authToken] = await Promise.all([props.params, getAuthToken()]);
27+
28+
const project = await getProject(params.team_slug, params.project_slug);
29+
30+
if (!authToken) {
31+
loginRedirect(`/team/${params.team_slug}/${params.project_slug}/payments`);
32+
}
33+
34+
if (!project) {
35+
redirect(`/team/${params.team_slug}`);
36+
}
37+
38+
const searchParams = await props.searchParams;
39+
40+
const { range, interval } = getUniversalBridgeFiltersFromSearchParams({
41+
defaultRange: "last-30",
42+
from: searchParams.from,
43+
interval: searchParams.interval,
44+
to: searchParams.to,
45+
});
46+
47+
const client = getClientThirdwebClient({
48+
jwt: authToken,
49+
teamId: project.teamId,
50+
});
51+
52+
return (
53+
<ProjectPage
54+
header={{
55+
client,
56+
title: "Bridge",
57+
icon: BridgeIcon,
58+
description: (
59+
<>
60+
Bridge allows you to enable seamless cross-chain swaps to move funds
61+
effortlessly across any EVM network
62+
</>
63+
),
64+
actions: {
65+
secondary: {
66+
href: `/team/${params.team_slug}/${params.project_slug}/webhooks/payments`,
67+
label: "Webhooks",
68+
icon: <WebhookIcon className="size-3.5 text-muted-foreground" />,
69+
},
70+
},
71+
links: [
72+
// TODO - add docs when bridge docs are added in portal
73+
// {
74+
// type: "docs",
75+
// href: "https://portal.thirdweb.com/payments",
76+
// },
77+
{
78+
type: "api",
79+
href: "https://api.thirdweb.com/reference#tag/bridge",
80+
},
81+
],
82+
}}
83+
footer={{
84+
center: {
85+
links: [
86+
{
87+
href: "https://playground.thirdweb.com/payments/ui-components",
88+
label: "UI Component",
89+
},
90+
{
91+
href: "https://playground.thirdweb.com/connect/payments/fund-wallet",
92+
label: "Buy Crypto",
93+
},
94+
{
95+
href: "https://playground.thirdweb.com/connect/payments/commerce",
96+
label: "Checkout",
97+
},
98+
{
99+
href: "https://playground.thirdweb.com/connect/payments/transactions",
100+
label: "Transactions",
101+
},
102+
],
103+
title: "Demos",
104+
},
105+
left: {
106+
links: [
107+
{
108+
href: "https://portal.thirdweb.com/payments",
109+
label: "Overview",
110+
},
111+
{
112+
href: "https://portal.thirdweb.com/typescript/v5/convertCryptoToFiat",
113+
label: "TypeScript",
114+
},
115+
{
116+
href: "https://portal.thirdweb.com/react/v5/pay/fund-wallets",
117+
label: "React",
118+
},
119+
{
120+
href: "https://portal.thirdweb.com/dotnet/universal-bridge/quickstart",
121+
label: ".NET",
122+
},
123+
],
124+
title: "Documentation",
125+
},
126+
right: {
127+
links: [
128+
{
129+
href: "https://www.youtube.com/watch?v=aBu175-VsNY",
130+
label: "Implement cross-chain payments in any app",
131+
},
132+
],
133+
title: "Tutorials",
134+
},
135+
}}
136+
>
137+
<div className="flex flex-col gap-12">
138+
<ResponsiveSearchParamsProvider value={searchParams}>
139+
<PayAnalytics
140+
client={client}
141+
interval={interval}
142+
projectClientId={project.publishableKey}
143+
projectId={project.id}
144+
range={range}
145+
teamId={project.teamId}
146+
authToken={authToken}
147+
/>
148+
</ResponsiveSearchParamsProvider>
149+
150+
<RouteDiscovery client={client} project={project} />
151+
152+
<QuickStartSection
153+
projectSlug={params.project_slug}
154+
teamSlug={params.team_slug}
155+
clientId={project.publishableKey}
156+
teamId={project.teamId}
157+
/>
158+
</div>
159+
</ProjectPage>
160+
);
161+
}

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,

0 commit comments

Comments
 (0)