diff --git a/apps/dashboard/src/@/icons/NebulaIcon.tsx b/apps/dashboard/src/@/icons/NebulaIcon.tsx
deleted file mode 100644
index 1a59d089f01..00000000000
--- a/apps/dashboard/src/@/icons/NebulaIcon.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-export function NebulaIcon(props: { className?: string }) {
- return (
-
- );
-}
diff --git a/apps/dashboard/src/@/icons/brand-icons/ExpoIcon.tsx b/apps/dashboard/src/@/icons/brand-icons/ExpoIcon.tsx
new file mode 100644
index 00000000000..3b45bc2cab5
--- /dev/null
+++ b/apps/dashboard/src/@/icons/brand-icons/ExpoIcon.tsx
@@ -0,0 +1,21 @@
+export function ExpoIcon(props: { className?: string }) {
+ return (
+
+ );
+}
diff --git a/apps/dashboard/src/@/icons/brand-icons/NextjsIcon.tsx b/apps/dashboard/src/@/icons/brand-icons/NextjsIcon.tsx
new file mode 100644
index 00000000000..0fd37729730
--- /dev/null
+++ b/apps/dashboard/src/@/icons/brand-icons/NextjsIcon.tsx
@@ -0,0 +1,59 @@
+export function NextjsIcon(props: { className?: string }) {
+ return (
+
+ );
+}
diff --git a/apps/dashboard/src/@/icons/brand-icons/NodeJSIcon.tsx b/apps/dashboard/src/@/icons/brand-icons/NodeJSIcon.tsx
new file mode 100644
index 00000000000..0f430bee249
--- /dev/null
+++ b/apps/dashboard/src/@/icons/brand-icons/NodeJSIcon.tsx
@@ -0,0 +1,20 @@
+export function NodeJSIcon(props: { className?: string }) {
+ return (
+
+ );
+}
diff --git a/apps/dashboard/src/@/icons/brand-icons/ViteIcon.tsx b/apps/dashboard/src/@/icons/brand-icons/ViteIcon.tsx
new file mode 100644
index 00000000000..d08367f5d9f
--- /dev/null
+++ b/apps/dashboard/src/@/icons/brand-icons/ViteIcon.tsx
@@ -0,0 +1,23 @@
+export function ViteIcon(props: { className?: string }) {
+ return (
+
+ );
+}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/components/server/products.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/components/server/products.ts
index b425f8219d1..fcdb457611c 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/components/server/products.ts
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/components/server/products.ts
@@ -1,7 +1,7 @@
+import { BotIcon } from "lucide-react";
import { ConnectSDKIcon } from "@/icons/ConnectSDKIcon";
import { ContractIcon } from "@/icons/ContractIcon";
import { EngineIcon } from "@/icons/EngineIcon";
-import { NebulaIcon } from "@/icons/NebulaIcon";
import { PayIcon } from "@/icons/PayIcon";
import { RPCIcon } from "@/icons/RPCIcon";
import { SmartAccountIcon } from "@/icons/SmartAccountIcon";
@@ -52,7 +52,7 @@ export const products = [
},
{
description: "The most powerful AI for interacting with the blockchain",
- icon: NebulaIcon,
+ icon: BotIcon,
id: "nebula",
link: "https://thirdweb.com/ai",
name: "thirdweb AI",
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/chart/AiTokenUsageChartCard.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/chart/AiTokenUsageChartCard.tsx
index b38e0505a6c..c5ca8895970 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/chart/AiTokenUsageChartCard.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/chart/AiTokenUsageChartCard.tsx
@@ -1,11 +1,11 @@
"use client";
import { format } from "date-fns";
+import { BotIcon } from "lucide-react";
import { useMemo } from "react";
import { ThirdwebBarChart } from "@/components/blocks/charts/bar-chart";
import { DocLink } from "@/components/blocks/DocLink";
import { ExportToCSVButton } from "@/components/blocks/ExportToCSVButton";
import type { ChartConfig } from "@/components/ui/chart";
-import { NebulaIcon } from "@/icons/NebulaIcon";
import type { AIUsageStats } from "@/types/analytics";
type ChartData = Record
& {
@@ -106,7 +106,7 @@ function AiTokenUsageEmptyChartState() {
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/page.tsx
index 2f818455eec..661426788dd 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/page.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/analytics/page.tsx
@@ -1,3 +1,4 @@
+import { BotIcon } from "lucide-react";
import { redirect } from "next/navigation";
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
import { getAuthToken } from "@/api/auth-token";
@@ -6,7 +7,6 @@ import type { DurationId } from "@/components/analytics/date-range-selector";
import { ResponsiveTimeFilters } from "@/components/analytics/responsive-time-filters";
import { ProjectPage } from "@/components/blocks/project-page/project-page";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
-import { NebulaIcon } from "@/icons/NebulaIcon";
import { getFiltersFromSearchParams } from "@/lib/time";
import { loginRedirect } from "@/utils/redirects";
import { AiAnalytics } from "./chart";
@@ -58,7 +58,7 @@ export default async function Page(props: {
-
+
thirdweb AI
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/Chats.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/Chats.tsx
index f5c41ed3440..7dc23ca4b67 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/Chats.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/Chats.tsx
@@ -1,10 +1,9 @@
import { MarkdownRenderer } from "@workspace/ui/components/markdown-renderer";
import { ScrollShadow } from "@workspace/ui/components/scroll-shadow";
-import { AlertCircleIcon } from "lucide-react";
+import { AlertCircleIcon, BotIcon } from "lucide-react";
import { useEffect, useRef } from "react";
import type { ThirdwebClient } from "thirdweb";
import type { Project } from "@/api/project/projects";
-import { NebulaIcon } from "@/icons/NebulaIcon";
import { cn } from "@/lib/utils";
import type {
NebulaSwapData,
@@ -238,11 +237,11 @@ function RenderMessage(props: {
)}
>
{message.type === "presence" && (
-
+
)}
{message.type === "assistant" && (
-
+
)}
{message.type === "error" && (
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
index b2614f74ee9..ad0c25a9578 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
@@ -1,9 +1,8 @@
"use client";
-import { ArrowUpRightIcon, InfoIcon } from "lucide-react";
+import { ArrowUpRightIcon, BotIcon, InfoIcon } from "lucide-react";
import type { ThirdwebClient } from "thirdweb";
import { Button } from "@/components/ui/button";
-import { NebulaIcon } from "@/icons/NebulaIcon";
import { cn } from "@/lib/utils";
import type { NebulaContext, NebulaUserMessage } from "../api/types";
import { examplePrompts } from "../data/examplePrompts";
@@ -33,7 +32,7 @@ export function EmptyStateChatPageContent(props: {
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ClientIDSection.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ClientIDSection.tsx
index 0dd8e8119f4..d9f9d0bc1f3 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ClientIDSection.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ClientIDSection.tsx
@@ -3,8 +3,8 @@ import { CopyTextButton } from "@/components/ui/CopyTextButton";
export function ClientIDSection(props: { clientId: string }) {
return (
-
Client ID
-
+
Client ID
+
Identifies your application
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/CodeSelector.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/CodeSelector.tsx
new file mode 100644
index 00000000000..4c6d4fd6e8d
--- /dev/null
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/CodeSelector.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import { useState } from "react";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+
+export function CodeShowcase(props: {
+ title: string;
+ tabs: Array<{
+ label: string;
+ code: React.ReactNode;
+ }>;
+}) {
+ const [selectedTab, setSelectedTab] = useState(props.tabs[0]?.label || "");
+
+ return (
+
+
+
{props.title}
+
+
+
+ {props.tabs.find((tab) => tab.label === selectedTab)?.code}
+
+ );
+}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.stories.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.stories.tsx
index e48d6d82575..190484c7c55 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.stories.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.stories.tsx
@@ -24,6 +24,7 @@ type Story = StoryObj
;
export const Default: Story = {
args: {
+ projectWalletSection: undefined,
project: {
...projectStub("foo", "bar"),
secretKeys: [
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx
index b1f51f3857f..5ddec808e09 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx
@@ -1,26 +1,40 @@
+import { Button } from "@workspace/ui/components/button";
+import { CodeServer } from "@workspace/ui/components/code/code.server";
import {
- ArrowLeftRightIcon,
+ ArrowUpRightIcon,
+ BotIcon,
ChevronRightIcon,
- ExternalLinkIcon,
+ CoinsIcon,
+ DoorOpenIcon,
} from "lucide-react";
import Link from "next/link";
import type { Project } from "@/api/project/projects";
-import { CodeServer } from "@/components/ui/code/code.server";
+import { BridgeIcon } from "@/icons/BridgeIcon";
import { DotNetIcon } from "@/icons/brand-icons/DotNetIcon";
-import { GithubIcon } from "@/icons/brand-icons/GithubIcon";
+import { ExpoIcon } from "@/icons/brand-icons/ExpoIcon";
+import { NextjsIcon } from "@/icons/brand-icons/NextjsIcon";
+import { NodeJSIcon } from "@/icons/brand-icons/NodeJSIcon";
import { ReactIcon } from "@/icons/brand-icons/ReactIcon";
import { TypeScriptIcon } from "@/icons/brand-icons/TypeScriptIcon";
import { UnityIcon } from "@/icons/brand-icons/UnityIcon";
import { UnrealIcon } from "@/icons/brand-icons/UnrealIcon";
-import { ContractIcon } from "@/icons/ContractIcon";
+import { ViteIcon } from "@/icons/brand-icons/ViteIcon";
import { PayIcon } from "@/icons/PayIcon";
+import { WalletProductIcon } from "@/icons/WalletProductIcon";
import { ClientIDSection } from "./ClientIDSection";
+import { CodeShowcase } from "./CodeSelector";
import { SecretKeySection } from "./SecretKeySection";
-export function ProjectFTUX(props: { project: Project; teamSlug: string }) {
+export function ProjectFTUX(props: {
+ project: Project;
+ teamSlug: string;
+ projectWalletSection: React.ReactNode;
+}) {
return (
-
+
+ {props.projectWalletSection}
+
-
- Integrate API key
-
+
+
+
API Keys
+
+ Your API key is used to authenticate and integrate your application.
+
+
+
-
+
{secretKeyMasked && (
@@ -53,23 +84,74 @@ function IntegrateAPIKeySection({ project }: { project: Project }) {
/>
)}
+
+
+ );
+}
-
-
-
- Run this command in your terminal to send a test transaction using
- your Project Wallet.
-
-
-
+function GetStartedSection({ project }: { project: Project }) {
+ return (
+
+
+
+ Get Started
+
+
+ Send a test transaction using your project wallet
+
+
+
+
+
+ ),
+ },
+ {
+ label: "JavaScript",
+ code: (
+
+ ),
+ },
+ {
+ label: "Python",
+ code: (
+
+ ),
+ },
+ ]}
+ />
);
}
+
const curlCodeExample = (project: Project): string => `\
curl https://api.thirdweb.com/v1/transactions \\
--request POST \\
@@ -87,6 +169,40 @@ curl https://api.thirdweb.com/v1/transactions \\
}'
`;
+const fetchJSCodeExample = (project: Project): string => `\
+fetch("https://github.com/service/https://api.thirdweb.com/v1/transactions", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "x-secret-key": "${project.secretKeys[0]?.masked ?? "
"}",
+ },
+ body: JSON.stringify({
+ chainId: 421614,
+ transactions: [
+ { data: "0x", to: "vitalik.eth", value: "0" },
+ ],
+ }),
+});
+`;
+
+const pythonCodeExample = (project: Project): string => `\
+import requests
+
+url = "https://api.thirdweb.com/v1/transactions"
+headers = {
+ "Content-Type": "application/json",
+ "x-secret-key": "${project.secretKeys[0]?.masked ?? ""}",
+}
+payload = {
+ "chainId": 421614,
+ "transactions": [
+ { "data": "0x", "to": "vitalik.eth", "value": "0" },
+ ],
+}
+response = requests.post(url, headers=headers, json=payload)
+result = response.json()
+`;
+
// products section ------------------------------------------------------------
function ProductsSection(props: { teamSlug: string; projectSlug: string }) {
@@ -97,49 +213,60 @@ function ProductsSection(props: { teamSlug: string; projectSlug: string }) {
icon: React.FC<{ className?: string }>;
}> = [
{
- description:
- "Scale your application with a backend server to read, write, and deploy contracts at production-grade.",
- href: `/team/${props.teamSlug}/${props.projectSlug}/transactions`,
- icon: ArrowLeftRightIcon,
- title: "Transactions",
+ icon: WalletProductIcon,
+ title: "Wallets",
+ description: "Wallets to read, write and transact",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/wallets/user-wallets`,
},
{
- description:
- "Deploy your own contracts or leverage existing solutions for onchain implementation",
- href: `/team/${props.teamSlug}/${props.projectSlug}/contracts`,
- icon: ContractIcon,
- title: "Contracts",
+ icon: BridgeIcon,
+ title: "Bridge",
+ description: "Swap and bridge tokens",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/bridge`,
},
{
- description:
- "Bridge, swap, and purchase cryptocurrencies with any fiat options or tokens via cross-chain routing",
- href: `/team/${props.teamSlug}/${props.projectSlug}/payments`,
icon: PayIcon,
- title: "Payments",
+ title: "x402",
+ description: "Native internet payments",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/x402`,
+ },
+ {
+ icon: DoorOpenIcon,
+ title: "Gateway",
+ description: "Blockchain connectivity and data access",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/gateway/rpc`,
+ },
+ {
+ icon: CoinsIcon,
+ title: "Tokens",
+ description: "Launch tokens and markets",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/tokens`,
+ },
+ {
+ icon: BotIcon,
+ title: "AI",
+ description: "Read and write onchain via AI agents",
+ href: `/team/${props.teamSlug}/${props.projectSlug}/ai`,
},
];
return (
-
- Complete your full-stack application
-
-
- Tools to build frontend, backend, and onchain with built-in
- infrastructure and analytics.
+
Products
+
+ Everything you need to build full-stack applications, games, and agents.
- {/* Feature Cards */}
-
+
{products.map((product) => (
-
))}
@@ -147,72 +274,51 @@ function ProductsSection(props: { teamSlug: string; projectSlug: string }) {
);
}
-function ProductCard(props: {
- title: string;
- description: string;
- href: string;
- icon: React.FC<{ className?: string }>;
-}) {
- return (
-
-
-
-
- {props.title}
-
-
-
{props.description}
-
- );
-}
-
// sdk section ------------------------------------------------------------
-type SDKCardProps = {
+type IconCardProps = {
name: string;
+ description: string | undefined;
href: string;
icon: React.FC<{ className?: string }>;
- trackingLabel: string;
};
-const sdks: SDKCardProps[] = [
+const sdks: IconCardProps[] = [
{
href: "https://portal.thirdweb.com/sdk/typescript",
icon: TypeScriptIcon,
name: "TypeScript",
- trackingLabel: "typescript",
+ description: undefined,
},
{
href: "https://portal.thirdweb.com/react/v5",
icon: ReactIcon,
name: "React",
- trackingLabel: "react",
+ description: undefined,
},
{
href: "https://portal.thirdweb.com/react-native/v5",
icon: ReactIcon,
name: "React Native",
- trackingLabel: "react_native",
+ description: undefined,
},
{
href: "https://portal.thirdweb.com/unity/v5",
icon: UnityIcon,
name: "Unity",
- trackingLabel: "unity",
+ description: undefined,
},
{
href: "https://portal.thirdweb.com/unreal-engine",
icon: UnrealIcon,
name: "Unreal Engine",
- trackingLabel: "unreal",
+ description: undefined,
},
{
href: "https://portal.thirdweb.com/dotnet",
icon: DotNetIcon,
name: ".NET",
- trackingLabel: "dotnet",
+ description: undefined,
},
];
@@ -220,14 +326,14 @@ function SDKSection() {
return (
Client SDKs
-
+
{sdks.map((sdk) => (
-
))}
@@ -235,10 +341,10 @@ function SDKSection() {
);
}
-function SDKCard(props: SDKCardProps) {
+function IconCard(props: IconCardProps) {
return (
-
-
+
+
@@ -246,16 +352,14 @@ function SDKCard(props: SDKCardProps) {
{props.name}
-
- View Docs
-
-
+ {props.description && (
+
{props.description}
+ )}
);
@@ -263,42 +367,47 @@ function SDKCard(props: SDKCardProps) {
// starter kits section ------------------------------------------------------------
-type StartedKitCardProps = {
- name: string;
- href: string;
- trackingLabel: string;
-};
-
-const startedKits: StartedKitCardProps[] = [
+const startedKits: IconCardProps[] = [
{
href: "https://github.com/thirdweb-example/next-starter",
name: "Next Starter",
- trackingLabel: "next_starter",
+ icon: NextjsIcon,
+ description: undefined,
},
{
href: "https://github.com/thirdweb-example/vite-starter",
name: "Vite Starter",
- trackingLabel: "vite_starter",
+ icon: ViteIcon,
+ description: undefined,
},
{
href: "https://github.com/thirdweb-example/expo-starter",
name: "Expo Starter",
- trackingLabel: "expo_starter",
+ icon: ExpoIcon,
+ description: undefined,
},
{
href: "https://github.com/thirdweb-example/node-starter",
name: "Node Starter",
- trackingLabel: "node_starter",
+ icon: NodeJSIcon,
+ description: undefined,
},
];
function StarterKitsSection() {
return (
-
-
Starter Kits
+
+
+
+ Starters
+
+
+ Kickstart your development process with ready-to-ship repositories.
+
+
-
+
{startedKits.map((kit) => (
-
))}
);
}
-
-function StarterKitCard(props: StartedKitCardProps) {
- return (
-
-
-
-
-
-
-
- {props.name}
-
-
- View Repo
-
-
-
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/SecretKeySection.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/SecretKeySection.tsx
index 3a6f24c9294..f7b6e4df883 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/SecretKeySection.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/SecretKeySection.tsx
@@ -13,15 +13,15 @@ export function SecretKeySection(props: {
return (
-
Secret Key
-
+
Secret Key
+
Identifies and authenticates your application from a backend.
{" "}
This is not the full secret key, Refer to your saved secret key at the
time of creation for the full secret key.
-
-
+
+
{secretKeyMasked}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
index 009229c4ac2..bd40487cb3e 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
@@ -1,11 +1,10 @@
"use client";
import { Badge } from "@workspace/ui/components/badge";
import {
- BookTextIcon,
- BoxIcon,
+ BotIcon,
DatabaseIcon,
+ DoorOpenIcon,
HomeIcon,
- RssIcon,
Settings2Icon,
WebhookIcon,
} from "lucide-react";
@@ -15,7 +14,6 @@ import {
} from "@/components/blocks/full-width-sidebar-layout";
import { BridgeIcon } from "@/icons/BridgeIcon";
import { ContractIcon } from "@/icons/ContractIcon";
-import { NebulaIcon } from "@/icons/NebulaIcon";
import { PayIcon } from "@/icons/PayIcon";
import { TokenIcon } from "@/icons/TokenIcon";
import { WalletProductIcon } from "@/icons/WalletProductIcon";
@@ -81,12 +79,12 @@ export function ProjectSidebarLayout(props: {
},
{
href: `${props.layoutPath}/ai`,
- icon: NebulaIcon,
+ icon: BotIcon,
label: "AI",
},
{
subMenu: {
- icon: RssIcon,
+ icon: DoorOpenIcon,
label: "Gateway",
},
links: [
@@ -129,19 +127,6 @@ export function ProjectSidebarLayout(props: {
icon: Settings2Icon,
label: "Project Settings",
},
- {
- separator: true,
- },
- {
- href: "https://portal.thirdweb.com/",
- icon: BookTextIcon,
- label: "Documentation",
- },
- {
- href: "https://playground.thirdweb.com/wallets/sign-in/button",
- icon: BoxIcon,
- label: "Playground",
- },
] satisfies ShadcnSidebarLink[];
return (
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx
index 819d0c652f4..38f5d8da1c1 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx
@@ -177,7 +177,7 @@ export function ProjectWalletDetailsSection(props: ProjectWalletControlsProps) {
-
+
-
+
Balance
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.stories.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.stories.tsx
index 750c51f3328..4d64cb51ca8 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.stories.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.stories.tsx
@@ -49,6 +49,7 @@ const projectWallet2: ProjectWalletSummary = {
export const NoProjectWalletSetNoManagedAccessToken: Story = {
args: {
+ layout: "column",
project: projectWithoutManagedAccessToken,
client: storybookThirdwebClient,
teamSlug: "bar",
@@ -62,6 +63,7 @@ export const NoProjectWalletSetNoManagedAccessToken: Story = {
export const NoProjectWalletSetWithManagedAccessToken: Story = {
args: {
+ layout: "column",
project: projectWithManagedAccessToken,
client: storybookThirdwebClient,
teamSlug: "bar",
@@ -75,6 +77,7 @@ export const NoProjectWalletSetWithManagedAccessToken: Story = {
export const NoProjectWalletSetWithManagedAccessTokenAndServerWallets: Story = {
args: {
+ layout: "column",
project: projectWithManagedAccessToken,
teamSlug: "bar",
client: storybookThirdwebClient,
@@ -88,6 +91,7 @@ export const NoProjectWalletSetWithManagedAccessTokenAndServerWallets: Story = {
export const NoProjectWalletSetLoading: Story = {
args: {
+ layout: "column",
project: projectWithManagedAccessToken,
teamSlug: "bar",
client: storybookThirdwebClient,
@@ -101,6 +105,7 @@ export const NoProjectWalletSetLoading: Story = {
export const ProjectWalletSetMultipleServerWallets: Story = {
args: {
+ layout: "column",
project: projectWithManagedAccessToken,
teamSlug: "bar",
client: storybookThirdwebClient,
@@ -114,6 +119,7 @@ export const ProjectWalletSetMultipleServerWallets: Story = {
export const ProjectWalletSetSingleServerWallet: Story = {
args: {
+ layout: "column",
project: projectWithManagedAccessToken,
teamSlug: "bar",
projectWallet: projectWallet1,
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.tsx
index e2a44ce2208..8175644e889 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet.tsx
@@ -2,7 +2,8 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { UnderlineLink } from "@workspace/ui/components/UnderlineLink";
-import { ChevronDownIcon, XIcon } from "lucide-react";
+import { ArrowUpRightIcon, ChevronDownIcon, XIcon } from "lucide-react";
+import Link from "next/link";
import { useMemo, useState } from "react";
import { toast } from "sonner";
import type { ThirdwebClient } from "thirdweb";
@@ -29,6 +30,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import type { ProjectWalletSummary } from "@/lib/server/project-wallet";
+import { cn } from "@/lib/utils";
import { updateDefaultProjectWallet } from "../../transactions/lib/vault.client";
import { CreateServerWallet } from "../../transactions/server-wallets/components/create-server-wallet.client";
import { ProjectWalletDetailsSection } from "./project-wallet-details";
@@ -131,11 +133,11 @@ function CreateProjectWalletSection(props: {
Boolean(managementAccessToken) && serverWallets.length > 0;
return (
-
+
-
+
-
+
No Project Wallet set
@@ -276,10 +278,17 @@ export function ProjectWalletSectionUI(props: {
projectWallet: ProjectWalletSummary | undefined;
getProjectServerWallets: GetProjectServerWallets;
client: ThirdwebClient;
+ layout: "row" | "column";
}) {
return (
-
-
+
+
Project Wallet
@@ -292,6 +301,24 @@ export function ProjectWalletSectionUI(props: {
thirdweb API
+ {props.layout === "row" && (
+
+
+
+ )}
{props.projectWallet ? (
@@ -318,6 +345,7 @@ export function ProjectWalletSection(props: {
teamSlug: string;
projectWallet: ProjectWalletSummary | undefined;
client: ThirdwebClient;
+ layout: "row" | "column";
}) {
return (
);
}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
index 3226572d485..ff2b14933a3 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
@@ -119,7 +119,7 @@ export default async function ProjectOverviewPage(props: PageProps) {
isProjectIcon: true,
icon: () => (
@@ -130,30 +130,44 @@ export default async function ProjectOverviewPage(props: PageProps) {
actions: null,
}}
>
-
-
-
-
{isActive ? (
-
) : (
-
+
)}
From b06a9184c10cc0caa40a0bf4895282576b31d094 Mon Sep 17 00:00:00 2001
From: MananTank
Date: Fri, 14 Nov 2025 21:08:47 +0000
Subject: [PATCH 3/4] [PRO-105] Dashboard: Update service names (#8419)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
## PR-Codex overview
This PR focuses on renaming and refactoring the usage of `products` to `services` across various components, updating service definitions, and improving the rendering of product cards in the UI.
### Detailed summary
- Renamed `products` to `services` in relevant components.
- Updated service definitions with new titles and descriptions.
- Modified the `ChainListRow` and `ChainServiceFilter` components to use `services`.
- Enhanced `SupportedProductsSection` to conditionally render `ProductCard`.
- Added new `ProductCard` component for UI rendering.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
---
apps/dashboard/src/@/icons/ConnectSDKIcon.tsx | 21 ---
.../server/SupportedProductsSection.tsx | 135 +++++++++++++-----
.../chainlist/components/client/filters.tsx | 6 +-
.../components/server/chainlist-row.tsx | 4 +-
.../(chain)/components/server/products.ts | 60 ++++----
packages/service-utils/src/core/services.ts | 12 +-
6 files changed, 135 insertions(+), 103 deletions(-)
delete mode 100644 apps/dashboard/src/@/icons/ConnectSDKIcon.tsx
diff --git a/apps/dashboard/src/@/icons/ConnectSDKIcon.tsx b/apps/dashboard/src/@/icons/ConnectSDKIcon.tsx
deleted file mode 100644
index fa1e9a2e7f8..00000000000
--- a/apps/dashboard/src/@/icons/ConnectSDKIcon.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-export function ConnectSDKIcon(props: { className?: string }) {
- return (
-
- );
-}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
index ec7ec06c3af..957e8c64255 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
@@ -1,55 +1,112 @@
+import { BotIcon, CodeIcon, CoinsIcon } from "lucide-react";
import Link from "next/link";
-import type { ChainMetadataWithServices } from "@/types/chain";
-import { products } from "../../../../components/server/products";
+import { useMemo } from "react";
+import { BridgeIcon } from "@/icons/BridgeIcon";
+import { PayIcon } from "@/icons/PayIcon";
+import { WalletProductIcon } from "@/icons/WalletProductIcon";
+import type {
+ ChainMetadataWithServices,
+ ChainSupportedService,
+} from "@/types/chain";
import { SectionTitle } from "./SectionTitle";
export function SupportedProductsSection(props: {
services: ChainMetadataWithServices["services"];
}) {
- const enabledProducts = products.filter((product) => {
- return props.services.find(
- (service) => service.service === product.id && service.enabled,
+ const enabledServices = useMemo(() => {
+ return props.services.reduce(
+ (acc, service) => {
+ acc[service.service] = service.enabled;
+ return acc;
+ },
+ {} as Record,
);
- });
-
- if (enabledProducts.length === 0) {
- return null;
- }
+ }, [props.services]);
return (
- {enabledProducts.map((product) => {
- return (
-
-
-
-
-
- {product.name}
-
-
-
- {product.description}
-
-
-
- );
- })}
+ {enabledServices["connect-sdk"] && (
+
+ )}
+
+ {enabledServices["account-abstraction"] && (
+
+ )}
+
+ {enabledServices.pay && (
+
+ )}
+
+ {enabledServices.contracts && (
+
+ )}
+
+
+
+
);
}
+
+function ProductCard(props: {
+ icon: React.FC<{ className?: string }>;
+ link: string;
+ name: string;
+ description: string;
+}) {
+ return (
+
+
+
+
+
+ {props.name}
+
+
+
{props.description}
+
+
+ );
+}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/client/filters.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/client/filters.tsx
index cb69e35cdd2..5a3f34c480a 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/client/filters.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/client/filters.tsx
@@ -15,7 +15,7 @@ import {
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Separator } from "@/components/ui/separator";
import { useDashboardRouter } from "@/lib/DashboardRouter";
-import { products } from "../../../components/server/products";
+import { services } from "../../../components/server/products";
function cleanUrl(url: string) {
if (url.endsWith("?")) {
@@ -345,7 +345,7 @@ export const ChainServiceFilter: React.FC = ({
const section = (
- {products.map((product) => (
+ {services.map((product) => (
= ({
}
const firstFilter = allFilters[0];
- const name = products.find((p) => p.id === firstFilter)?.name;
+ const name = services.find((p) => p.id === firstFilter)?.name;
const plus = allFilters.length > 1 ? ` +${allFilters.length - 1}` : "";
return [`${name}${plus}`, true];
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
index fefb8e26749..01dc2d26000 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
@@ -12,7 +12,7 @@ import { ToolTipLabel } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import type { ChainSupportedService } from "@/types/chain";
import { ChainIcon } from "../../../components/server/chain-icon";
-import { products } from "../../../components/server/products";
+import { services } from "../../../components/server/products";
import { getCustomChainMetadata } from "../../../utils";
type ChainListRowProps = {
@@ -83,7 +83,7 @@ export function ChainListRow({
- {products.map((p) => {
+ {services.map((p) => {
return (
;
- description: string;
link: string;
}>;
diff --git a/packages/service-utils/src/core/services.ts b/packages/service-utils/src/core/services.ts
index ffaead9ae84..bdcb373370d 100644
--- a/packages/service-utils/src/core/services.ts
+++ b/packages/service-utils/src/core/services.ts
@@ -4,7 +4,7 @@ export const SERVICE_DEFINITIONS = {
actions: [],
description: "Bundler & Paymaster services",
name: "bundler",
- title: "Account Abstraction",
+ title: "Gas Sponsorship",
},
chainsaw: {
// all actions allowed
@@ -18,7 +18,7 @@ export const SERVICE_DEFINITIONS = {
actions: [],
description: "E-mail and social login wallets for easy web3 onboarding",
name: "embeddedWallets",
- title: "Wallets",
+ title: "User Wallets",
},
engineCloud: {
@@ -27,14 +27,14 @@ export const SERVICE_DEFINITIONS = {
description:
"Transaction API and Server wallets with high transaction throughput and low latency",
name: "engineCloud",
- title: "Transactions",
+ title: "Server Wallets",
},
insight: {
// all actions allowed
actions: [],
description: "Indexed data for any EVM chain",
name: "insight",
- title: "Insight",
+ title: "Indexer",
},
nebula: {
// all actions allowed
@@ -42,7 +42,7 @@ export const SERVICE_DEFINITIONS = {
description:
"Advanced blockchain reasoning and execution capabilities with AI",
name: "nebula",
- title: "thirdweb AI",
+ title: "AI",
},
pay: {
// all actions allowed
@@ -50,7 +50,7 @@ export const SERVICE_DEFINITIONS = {
description:
"Bridge, swap, and purchase cryptocurrencies and execute transactions with any fiat or tokens via cross-chain routing",
name: "pay",
- title: "Payments",
+ title: "Bridge",
},
relayer: {
// all actions allowed
From c7ff3b1020034b9061b668de464e491efded7bb3 Mon Sep 17 00:00:00 2001
From: Jonas Daniels
Date: Fri, 14 Nov 2025 14:59:40 -0800
Subject: [PATCH 4/4] Add /gateway to framer-rewrites.js
---
apps/dashboard/framer-rewrites.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/apps/dashboard/framer-rewrites.js b/apps/dashboard/framer-rewrites.js
index a51116e63b7..c957cd5177e 100644
--- a/apps/dashboard/framer-rewrites.js
+++ b/apps/dashboard/framer-rewrites.js
@@ -21,6 +21,7 @@ module.exports = [
"/rpc",
"/insight",
"/storage",
+ "/gateway",
// -- end scale category
// -- ai