Skip to content

Commit 08eea95

Browse files
committed
[TOOL-3764] Dashboard: Add Nebula FTUX (#6616)
<!-- ## 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 introduces a new `ClientIDSection` component to streamline the display of the Client ID across various components. It also modifies the `WaitingForIntegrationCard` to utilize this new component, improving code organization and reducing redundancy. ### Detailed summary - Added `ClientIDSection` component to display the Client ID. - Updated `InsightFTUX` to include `ClientIDSection` instead of inline code. - Modified `WaitingForIntegrationCard` to accept children for rendering. - Created `NebulaFTUX` component to integrate Nebula features and display Client ID. - Adjusted styles in various components for consistency. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 737155b commit 08eea95

File tree

8 files changed

+170
-46
lines changed

8 files changed

+170
-46
lines changed

apps/dashboard/src/app/account/contracts/_components/DeployViaCLIOrImportCard.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function DeployViaCLIOrImportCard(props: {
1313
const [importModalOpen, setImportModalOpen] = useState(false);
1414

1515
return (
16-
<div className="rounded-lg border bg-card p-6">
16+
<div className="rounded-lg border bg-card p-4 lg:p-6">
1717
<ImportModal
1818
isOpen={importModalOpen}
1919
onClose={() => {
@@ -26,14 +26,18 @@ export function DeployViaCLIOrImportCard(props: {
2626
<h2 className="mb-0.5 font-semibold text-lg">
2727
Already have a smart contract?
2828
</h2>
29-
<p className="max-w-2xl text-muted-foreground">
29+
<p className="max-w-2xl text-muted-foreground text-sm lg:text-base">
3030
Import an already deployed contract or deploy a contract from source
3131
code to easily manage permissions, upload assets, and interact with
3232
contract functions
3333
</p>
3434

35-
<div className="mt-6 flex gap-3">
36-
<Button variant="outline" className="gap-2 lg:px-20" asChild>
35+
<div className="mt-6 flex flex-col gap-3 lg:flex-row">
36+
<Button
37+
variant="outline"
38+
className="gap-2 bg-background lg:px-10"
39+
asChild
40+
>
3741
<Link
3842
href="https://portal.thirdweb.com/contracts/deploy/overview"
3943
target="_blank"
@@ -44,7 +48,7 @@ export function DeployViaCLIOrImportCard(props: {
4448
</Button>
4549
<Button
4650
variant="outline"
47-
className="gap-2 lg:px-6"
51+
className="gap-2 bg-background"
4852
onClick={() => setImportModalOpen(true)}
4953
>
5054
<DownloadIcon className="size-4" />
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { CopyTextButton } from "@/components/ui/CopyTextButton";
2+
3+
export function ClientIDSection(props: {
4+
clientId: string;
5+
}) {
6+
return (
7+
<div>
8+
<h3>Client ID</h3>
9+
<p className="mb-2 text-muted-foreground text-sm">
10+
Identifies your application
11+
</p>
12+
13+
<CopyTextButton
14+
textToCopy={props.clientId}
15+
className="!h-auto w-full max-w-[400px] justify-between truncate bg-background px-3 py-3 font-mono"
16+
textToShow={props.clientId}
17+
copyIconPosition="right"
18+
tooltip="Copy Client ID"
19+
/>
20+
</div>
21+
);
22+
}

apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/ProjectFTUX/IntegrateAPIKeyCodeTabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function IntegrateAPIKeyCodeTabs(props: {
2929
isActive: tab === key,
3030
}))}
3131
/>
32-
<div className="h-3" />
32+
<div className="h-2" />
3333
{props.tabs[tab]}
3434
</div>
3535
);

apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/ProjectFTUX/ProjectFTUX.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { Project } from "@/api/projects";
2-
import { CopyTextButton } from "@/components/ui/CopyTextButton";
32
import { UnderlineLink } from "@/components/ui/UnderlineLink";
43
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
54
import { CodeServer } from "@/components/ui/code/code.server";
@@ -19,6 +18,7 @@ import { TypeScriptIcon } from "../../../../../../components/icons/brand-icons/T
1918
import { UnityIcon } from "../../../../../../components/icons/brand-icons/UnityIcon";
2019
import { UnrealIcon } from "../../../../../../components/icons/brand-icons/UnrealIcon";
2120
import { NebulaIcon } from "../../../../../nebula-app/(app)/icons/NebulaIcon";
21+
import { ClientIDSection } from "./ClientIDSection";
2222
import { IntegrateAPIKeyCodeTabs } from "./IntegrateAPIKeyCodeTabs";
2323
import { SecretKeySection } from "./SecretKeySection";
2424

@@ -59,21 +59,7 @@ function IntegrateAPIKeySection({
5959

6060
<div className="rounded-lg border border-border bg-card p-4">
6161
<div className="flex flex-col gap-6 ">
62-
<div>
63-
<h3>Client ID</h3>
64-
<p className="mb-2 text-muted-foreground text-sm">
65-
Identifies your application
66-
</p>
67-
68-
<CopyTextButton
69-
textToCopy={clientId}
70-
className="!h-auto w-full max-w-[350px] justify-between truncate bg-background px-3 py-3 font-mono"
71-
textToShow={clientId}
72-
copyIconPosition="right"
73-
tooltip="Copy Client ID"
74-
/>
75-
</div>
76-
62+
<ClientIDSection clientId={clientId} />
7763
{secretKeyMasked && (
7864
<SecretKeySection
7965
secretKeyMasked={secretKeyMasked}

apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/WaitingForIntegrationCard/WaitingForIntegrationCard.tsx

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
"use client";
2-
3-
import { CopyTextButton } from "@/components/ui/CopyTextButton";
42
import { Button } from "@/components/ui/button";
53
import { TabButtons } from "@/components/ui/tabs";
64
import { TrackedLinkTW } from "@/components/ui/tracked-link";
@@ -9,7 +7,6 @@ import { useState } from "react";
97

108
export function WaitingForIntegrationCard(props: {
119
title: string;
12-
clientId: string;
1310
codeTabs: {
1411
code: React.ReactNode;
1512
label: string;
@@ -18,7 +15,9 @@ export function WaitingForIntegrationCard(props: {
1815
label: string;
1916
href: string;
2017
trackingLabel: string;
18+
category: string;
2119
}[];
20+
children?: React.ReactNode;
2221
}) {
2322
const [selectedTab, setSelectedTab] = useState(props.codeTabs[0]?.label);
2423
return (
@@ -28,24 +27,7 @@ export function WaitingForIntegrationCard(props: {
2827
</div>
2928

3029
<div className="px-4 py-6 lg:p-6">
31-
{/* Client ID */}
32-
<div className="flex w-full flex-col">
33-
<h3 className="font-medium">Client ID</h3>
34-
<p className="mb-2 text-muted-foreground text-sm">
35-
Identifies your application
36-
</p>
37-
38-
<CopyTextButton
39-
textToCopy={props.clientId}
40-
className="!h-auto w-full max-w-[400px] justify-between truncate bg-background px-3 py-3 font-mono"
41-
textToShow={props.clientId}
42-
copyIconPosition="right"
43-
tooltip="Copy Client ID"
44-
/>
45-
</div>
46-
47-
<div className="h-4" />
48-
30+
{props.children}
4931
{/* Code */}
5032
<div>
5133
<TabButtons
@@ -70,7 +52,7 @@ export function WaitingForIntegrationCard(props: {
7052
key={cta.label}
7153
target="_blank"
7254
className="gap-2"
73-
category="insight-ftux"
55+
category={cta.category}
7456
label={cta.trackingLabel}
7557
>
7658
{cta.label}

apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/insight-ftux.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { CodeServer } from "@/components/ui/code/code.server";
22
import { isProd } from "@/constants/env";
3+
import { ClientIDSection } from "../components/ProjectFTUX/ClientIDSection";
34
import { WaitingForIntegrationCard } from "../components/WaitingForIntegrationCard/WaitingForIntegrationCard";
45

56
export function InsightFTUX(props: {
@@ -8,7 +9,6 @@ export function InsightFTUX(props: {
89
return (
910
<WaitingForIntegrationCard
1011
title="Integrate Insight"
11-
clientId={props.clientId}
1212
codeTabs={[
1313
{
1414
label: "JavaScript",
@@ -46,14 +46,19 @@ export function InsightFTUX(props: {
4646
label: "Try on Playground",
4747
href: "https://playground.thirdweb.com/insight",
4848
trackingLabel: "playground",
49+
category: "insight-ftux",
4950
},
5051
{
5152
label: "View Docs",
5253
href: "https://portal.thirdweb.com/insight",
5354
trackingLabel: "docs",
55+
category: "insight-ftux",
5456
},
5557
]}
56-
/>
58+
>
59+
<ClientIDSection clientId={props.clientId} />
60+
<div className="h-4" />
61+
</WaitingForIntegrationCard>
5762
);
5863
}
5964

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { CodeServer } from "@/components/ui/code/code.server";
2+
import { SecretKeySection } from "../components/ProjectFTUX/SecretKeySection";
3+
import { WaitingForIntegrationCard } from "../components/WaitingForIntegrationCard/WaitingForIntegrationCard";
4+
5+
export function NebulaFTUX(props: {
6+
secretKeyMasked: string;
7+
projectId: string;
8+
}) {
9+
return (
10+
<WaitingForIntegrationCard
11+
title="Integrate Nebula"
12+
codeTabs={[
13+
{
14+
label: "JavaScript",
15+
code: (
16+
<CodeServer code={jsCode} className="bg-background" lang="ts" />
17+
),
18+
},
19+
{
20+
label: "Python",
21+
code: (
22+
<CodeServer
23+
code={pythonCode}
24+
className="bg-background"
25+
lang="python"
26+
/>
27+
),
28+
},
29+
{
30+
label: "Curl",
31+
code: (
32+
<CodeServer code={curlCode} className="bg-background" lang="bash" />
33+
),
34+
},
35+
]}
36+
ctas={[
37+
{
38+
label: "Try on Playground",
39+
href: "https://nebula.thirdweb.com/",
40+
trackingLabel: "playground",
41+
category: "nebula-ftux",
42+
},
43+
{
44+
label: "View Docs",
45+
href: "https://portal.thirdweb.com/nebula",
46+
trackingLabel: "docs",
47+
category: "nebula-ftux",
48+
},
49+
]}
50+
>
51+
<SecretKeySection
52+
secretKeyMasked={props.secretKeyMasked}
53+
projectId={props.projectId}
54+
/>
55+
<div className="h-4" />
56+
</WaitingForIntegrationCard>
57+
);
58+
}
59+
60+
const jsCode = `\
61+
// Example: Send message to Nebula
62+
// Replace PROJECT_SECRET_KEY with your project's full secret key
63+
64+
const res = await fetch("https://nebula-api.thirdweb.com/chat", {
65+
method: "POST",
66+
headers: {
67+
"x-secret-key": "PROJECT_SECRET_KEY",
68+
},
69+
body: {
70+
message: "Hello",
71+
stream: false,
72+
},
73+
});
74+
75+
const data = await res.json();
76+
`;
77+
78+
const curlCode = `\
79+
# Example: Send message to Nebula
80+
# Replace PROJECT_SECRET_KEY with your project's full secret key
81+
82+
curl -X POST https://nebula-api.thirdweb.com/chat \
83+
-H "x-secret-key:PROJECT_SECRET_KEY" \
84+
-d '{
85+
"message": "Hello",
86+
"stream": false,
87+
}'
88+
`;
89+
90+
const pythonCode = `\
91+
# Example: Send message to Nebula
92+
# Replace PROJECT_SECRET_KEY with your project's full secret key
93+
94+
import requests
95+
96+
response = requests.post("https://nebula-api.thirdweb.com/chat", headers={
97+
"x-secret-key": "PROJECT_SECRET_KEY"
98+
}, json={
99+
"message": "Hello",
100+
"stream": False,
101+
})
102+
103+
data = response.json()
104+
`;

apps/dashboard/src/app/team/[team_slug]/[project_slug]/nebula/page.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { isProjectActive } from "@/api/analytics";
12
import { getProject } from "@/api/projects";
23
import { getTeamBySlug } from "@/api/team";
34
import { redirect } from "next/navigation";
45
import { getAuthToken } from "../../../../api/lib/getAuthToken";
56
import { loginRedirect } from "../../../../login/loginRedirect";
67
import { NebulaAnalyticsPage } from "./components/analytics/nebula-analytics-page";
78
import { NebulaWaitListPage } from "./components/nebula-waitlist-page";
9+
import { NebulaFTUX } from "./nebula-ftux";
810

911
export default async function Page(props: {
1012
params: Promise<{
@@ -42,7 +44,26 @@ export default async function Page(props: {
4244

4345
const hasNebulaAccess = team.enabledScopes.includes("nebula");
4446

47+
const activeResponse = await isProjectActive({
48+
teamId: team.id,
49+
projectId: project.id,
50+
});
51+
52+
const showFTUX = !activeResponse.nebula;
53+
4554
if (hasNebulaAccess) {
55+
if (showFTUX) {
56+
return (
57+
<div>
58+
<h1 className="mb-5 font-semibold text-3xl tracking-tight">Nebula</h1>
59+
<NebulaFTUX
60+
secretKeyMasked={project.secretKeys[0]?.masked || ""}
61+
projectId={project.id}
62+
/>
63+
</div>
64+
);
65+
}
66+
4667
return (
4768
<NebulaAnalyticsPage
4869
teamId={team.id}

0 commit comments

Comments
 (0)