Skip to content

Commit 4ca407d

Browse files
committed
[MNY-139] Dashboard, Portal, Playground: Add Bridge Product (#7984)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the payment and bridge functionalities in the dashboard and playground applications. It includes UI improvements, new components, and refactoring for better organization and usability. ### Detailed summary - Deleted `RouteDiscoveryCard.tsx`. - Replaced `BridgeIcon` export. - Added route rewrites in `next.config.mjs`. - Updated navigation links in `navLinks.ts`. - Modified `useTokensData.ts` to include price parameters. - Refactored `CreatePaymentWebhookButton` to a function. - Enhanced layout and styling in various components, including `ProjectSidebarLayout`, `EmptyState`, and `QuickstartSection`. - Introduced `ProjectSettingsBreadcrumb` for better navigation. - Added support for locked widgets in payment components. - Improved `PaymentLinksTable` and `PaymentHistory` components with new table structures. - Enhanced `FeatureCard` and `PayAnalytics` with updated styling and structure. - Created `CheckoutPlayground` and `TransactionPlayground` components for better payment handling. - Reorganized the bridge section with improved UI and functionality. > The following files were skipped due to too many changes: `apps/playground-web/src/app/payments/transactions/page.tsx` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Bridge page with analytics, route discovery, and Quickstart; CSV export for payment history; new Buy, Checkout, and Transaction playgrounds. * **Navigation** * Added "Bridge" to project sidebar; Webhooks consolidated with tabs; Payments page streamlined; redirect from /payments/ui-components → /payments and removed UI Components page. * **UI/UX Improvements** * Redesigned analytics and payment history table; updated FeatureCard and EmptyState visuals; removed "Swap Tokens" card; settings pages use breadcrumbs; assorted copy and spacing tweaks. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent f714e5d commit 4ca407d

File tree

39 files changed

+888
-726
lines changed

39 files changed

+888
-726
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={(chainId) => {
122+
field.onChange(chainId, { 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}/bridge`);
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 lets developers swap and transfer any token across any chain
61+
instantly
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)