Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,15 @@ type Action =
component: React.ReactNode;
};

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

return "component" in action ? (
action.component
) : (
<Button
asChild
className={cn(
"rounded-full",
props.variant === "secondary" && "border border-border",
)}
className={cn("rounded-full")}
size="sm"
variant={props.variant}
>
Expand All @@ -60,7 +57,7 @@ export type ProjectPageHeaderProps = {
icon: React.FC<{ className?: string }>;
isProjectIcon?: boolean;
actions: {
primary: Action;
primary?: Action;
secondary?: Action;
} | null;

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

{props.actions.primary && (
Expand All @@ -132,7 +129,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
{props.title}
</h2>
{/* description */}
<p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2">
<p className="text-sm text-muted-foreground text-pretty">
{props.description}
</p>
</div>
Expand All @@ -145,7 +142,7 @@ export function ProjectPageHeader(props: ProjectPageHeaderProps) {
)}

{props.actions?.secondary && (
<Action action={props.actions.secondary} variant="secondary" />
<Action action={props.actions.secondary} variant="outline" />
)}
</div>
)}
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/src/@/icons/BridgeIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { BringToFrontIcon as BridgeIcon } from "lucide-react";
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use client";

import {
ArrowRightLeftIcon,
BadgeDollarSignIcon,
WebhookIcon,
} from "lucide-react";
import { FeatureCard } from "../payments/components/FeatureCard.client";

export function QuickStartSection(props: {
teamSlug: string;
projectSlug: string;
clientId: string;
teamId: string;
}) {
return (
<section>
<div className="mb-4">
<h2 className="font-semibold text-xl tracking-tight">Get Started</h2>
<p className="text-muted-foreground text-sm">
Integrate bridge into your project and enable seamless cross-chain
swaps
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<FeatureCard
title="Cross-chain Swap Tokens"
icon={ArrowRightLeftIcon}
setupTime={5}
id="swap_tokens"
features={["Swap any token", "Cross-chain swaps"]}
description="Swap tokens cross-chain with dedicated swapping endpoints."
link={{
href: `https://portal.thirdweb.com/payments/swap`,
label: "Setup Swaps",
}}
/>

<FeatureCard
title="Earn Fees"
description="Setup fees to earn any time a user swaps or bridges funds."
icon={BadgeDollarSignIcon}
id="fees"
setupTime={1}
features={[
"Fees on every purchase",
"Custom percentage",
"Directly to your wallet",
]}
link={{
href: `/team/${props.teamSlug}/${props.projectSlug}/settings/payments`,
label: "Configure Fees",
}}
/>

<FeatureCard
title="Webhooks"
description="Create Webhooks to get notified on each purchase or transaction."
icon={WebhookIcon}
setupTime={5}
id="webhooks"
features={["Instant events", "Transaction verification"]}
link={{
href: `/team/${props.teamSlug}/${props.projectSlug}/webhooks/payments`,
label: "Setup Webhooks",
}}
/>
</div>
</section>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner";
import type { ThirdwebClient } from "thirdweb";
import { addUniversalBridgeTokenRoute } from "@/api/universal-bridge/tokens"; // Adjust the import path
import { RouteDiscoveryCard } from "@/components/blocks/RouteDiscoveryCard";
import { NetworkSelectorButton } from "@/components/misc/NetworkSelectorButton";
import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
import {
Form,
FormControl,
Expand All @@ -20,6 +19,7 @@ import {
type RouteDiscoveryValidationSchema,
routeDiscoveryValidationSchema,
} from "@/schema/validations";
import { RouteDiscoveryCard } from "./RouteDiscoveryCard";

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

<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 max-w-3xl">
<FormField
control={form.control}
name="chainId"
render={({ field }) => (
<FormItem>
<FormLabel>Blockchain</FormLabel>
<FormLabel>Chain</FormLabel>
<FormControl>
<NetworkSelectorButton
<SingleNetworkSelector
chainId={field.value}
className="bg-background"
disableChainId
disableDeprecated
client={client}
onSwitchChain={(chain) => {
// Update the form field value
field.onChange(chain.chainId);
onChange={(chainId) => {
field.onChange(chainId, { shouldValidate: true });
}}
/>
</FormControl>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { WebhookIcon } from "lucide-react";
import { redirect } from "next/navigation";
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
import { getAuthToken } from "@/api/auth-token";
import { getProject } from "@/api/project/projects";
import { ProjectPage } from "@/components/blocks/project-page/project-page";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { BridgeIcon } from "@/icons/BridgeIcon";
import { loginRedirect } from "@/utils/redirects";
import { PayAnalytics } from "../payments/components/PayAnalytics";
import { getUniversalBridgeFiltersFromSearchParams } from "../payments/components/time";
import { QuickStartSection } from "./QuickstartSection.client";
import { RouteDiscovery } from "./RouteDiscovery";

export default async function Page(props: {
params: Promise<{
team_slug: string;
project_slug: string;
}>;
searchParams: Promise<{
from?: string | undefined | string[];
to?: string | undefined | string[];
interval?: string | undefined | string[];
}>;
}) {
const [params, authToken] = await Promise.all([props.params, getAuthToken()]);

const project = await getProject(params.team_slug, params.project_slug);

if (!authToken) {
loginRedirect(`/team/${params.team_slug}/${params.project_slug}/bridge`);
}

if (!project) {
redirect(`/team/${params.team_slug}`);
}

const searchParams = await props.searchParams;

const { range, interval } = getUniversalBridgeFiltersFromSearchParams({
defaultRange: "last-30",
from: searchParams.from,
interval: searchParams.interval,
to: searchParams.to,
});

const client = getClientThirdwebClient({
jwt: authToken,
teamId: project.teamId,
});

return (
<ProjectPage
header={{
client,
title: "Bridge",
icon: BridgeIcon,
description: (
<>
Bridge lets developers swap and transfer any token across any chain
instantly
</>
),
actions: {
secondary: {
href: `/team/${params.team_slug}/${params.project_slug}/webhooks/payments`,
label: "Webhooks",
icon: <WebhookIcon className="size-3.5 text-muted-foreground" />,
},
},
links: [
// TODO - add docs when bridge docs are added in portal
// {
// type: "docs",
// href: "https://portal.thirdweb.com/payments",
// },
{
type: "api",
href: "https://api.thirdweb.com/reference#tag/bridge",
},
],
}}
footer={{
center: {
links: [
{
href: "https://playground.thirdweb.com/payments/ui-components",
label: "UI Component",
},
{
href: "https://playground.thirdweb.com/connect/payments/fund-wallet",
label: "Buy Crypto",
},
{
href: "https://playground.thirdweb.com/connect/payments/commerce",
label: "Checkout",
},
{
href: "https://playground.thirdweb.com/connect/payments/transactions",
label: "Transactions",
},
],
title: "Demos",
},
left: {
links: [
{
href: "https://portal.thirdweb.com/payments",
label: "Overview",
},
{
href: "https://portal.thirdweb.com/typescript/v5/convertCryptoToFiat",
label: "TypeScript",
},
{
href: "https://portal.thirdweb.com/react/v5/pay/fund-wallets",
label: "React",
},
{
href: "https://portal.thirdweb.com/dotnet/universal-bridge/quickstart",
label: ".NET",
},
],
title: "Documentation",
},
right: {
links: [
{
href: "https://www.youtube.com/watch?v=aBu175-VsNY",
label: "Implement cross-chain payments in any app",
},
],
title: "Tutorials",
},
}}
>
<div className="flex flex-col gap-12">
<ResponsiveSearchParamsProvider value={searchParams}>
<PayAnalytics
client={client}
interval={interval}
projectClientId={project.publishableKey}
projectId={project.id}
range={range}
teamId={project.teamId}
authToken={authToken}
/>
</ResponsiveSearchParamsProvider>

<RouteDiscovery client={client} project={project} />

<QuickStartSection
projectSlug={params.project_slug}
teamSlug={params.team_slug}
clientId={project.publishableKey}
teamId={project.teamId}
/>
</div>
</ProjectPage>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "lucide-react";
import { FullWidthSidebarLayout } from "@/components/blocks/full-width-sidebar-layout";
import { Badge } from "@/components/ui/badge";
import { BridgeIcon } from "@/icons/BridgeIcon";
import { ContractIcon } from "@/icons/ContractIcon";
import { InsightIcon } from "@/icons/InsightIcon";
import { NebulaIcon } from "@/icons/NebulaIcon";
Expand Down Expand Up @@ -72,6 +73,11 @@ export function ProjectSidebarLayout(props: {
icon: PayIcon,
label: "Payments",
},
{
href: `${props.layoutPath}/bridge`,
icon: BridgeIcon,
label: "Bridge",
},
{
href: `${props.layoutPath}/tokens`,
icon: TokenIcon,
Expand Down
Loading
Loading