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 @@ -13,7 +13,7 @@ export function DeployViaCLIOrImportCard(props: {
const [importModalOpen, setImportModalOpen] = useState(false);

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

<div className="mt-6 flex gap-3">
<Button variant="outline" className="gap-2 lg:px-20" asChild>
<div className="mt-6 flex flex-col gap-3 lg:flex-row">
<Button
variant="outline"
className="gap-2 bg-background lg:px-10"
asChild
>
<Link
href="https://portal.thirdweb.com/contracts/deploy/overview"
target="_blank"
Expand All @@ -44,7 +48,7 @@ export function DeployViaCLIOrImportCard(props: {
</Button>
<Button
variant="outline"
className="gap-2 lg:px-6"
className="gap-2 bg-background"
onClick={() => setImportModalOpen(true)}
>
<DownloadIcon className="size-4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { CopyTextButton } from "@/components/ui/CopyTextButton";

export function ClientIDSection(props: {
clientId: string;
}) {
return (
<div>
<h3>Client ID</h3>
<p className="mb-2 text-muted-foreground text-sm">
Identifies your application
</p>

<CopyTextButton
textToCopy={props.clientId}
className="!h-auto w-full max-w-[400px] justify-between truncate bg-background px-3 py-3 font-mono"
textToShow={props.clientId}
copyIconPosition="right"
tooltip="Copy Client ID"
/>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function IntegrateAPIKeyCodeTabs(props: {
isActive: tab === key,
}))}
/>
<div className="h-3" />
<div className="h-2" />
{props.tabs[tab]}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Project } from "@/api/projects";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { UnderlineLink } from "@/components/ui/UnderlineLink";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { CodeServer } from "@/components/ui/code/code.server";
Expand All @@ -19,6 +18,7 @@ import { TypeScriptIcon } from "../../../../../../components/icons/brand-icons/T
import { UnityIcon } from "../../../../../../components/icons/brand-icons/UnityIcon";
import { UnrealIcon } from "../../../../../../components/icons/brand-icons/UnrealIcon";
import { NebulaIcon } from "../../../../../nebula-app/(app)/icons/NebulaIcon";
import { ClientIDSection } from "./ClientIDSection";
import { IntegrateAPIKeyCodeTabs } from "./IntegrateAPIKeyCodeTabs";
import { SecretKeySection } from "./SecretKeySection";

Expand Down Expand Up @@ -59,21 +59,7 @@ function IntegrateAPIKeySection({

<div className="rounded-lg border border-border bg-card p-4">
<div className="flex flex-col gap-6 ">
<div>
<h3>Client ID</h3>
<p className="mb-2 text-muted-foreground text-sm">
Identifies your application
</p>

<CopyTextButton
textToCopy={clientId}
className="!h-auto w-full max-w-[350px] justify-between truncate bg-background px-3 py-3 font-mono"
textToShow={clientId}
copyIconPosition="right"
tooltip="Copy Client ID"
/>
</div>

<ClientIDSection clientId={clientId} />
{secretKeyMasked && (
<SecretKeySection
secretKeyMasked={secretKeyMasked}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use client";

import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Button } from "@/components/ui/button";
import { TabButtons } from "@/components/ui/tabs";
import { TrackedLinkTW } from "@/components/ui/tracked-link";
Expand All @@ -9,7 +7,6 @@ import { useState } from "react";

export function WaitingForIntegrationCard(props: {
title: string;
clientId: string;
codeTabs: {
code: React.ReactNode;
label: string;
Expand All @@ -18,7 +15,9 @@ export function WaitingForIntegrationCard(props: {
label: string;
href: string;
trackingLabel: string;
category: string;
}[];
children?: React.ReactNode;
}) {
const [selectedTab, setSelectedTab] = useState(props.codeTabs[0]?.label);
return (
Expand All @@ -28,24 +27,7 @@ export function WaitingForIntegrationCard(props: {
</div>

<div className="px-4 py-6 lg:p-6">
{/* Client ID */}
<div className="flex w-full flex-col">
<h3 className="font-medium">Client ID</h3>
<p className="mb-2 text-muted-foreground text-sm">
Identifies your application
</p>

<CopyTextButton
textToCopy={props.clientId}
className="!h-auto w-full max-w-[400px] justify-between truncate bg-background px-3 py-3 font-mono"
textToShow={props.clientId}
copyIconPosition="right"
tooltip="Copy Client ID"
/>
</div>

<div className="h-4" />

{props.children}
{/* Code */}
<div>
<TabButtons
Expand All @@ -70,7 +52,7 @@ export function WaitingForIntegrationCard(props: {
key={cta.label}
target="_blank"
className="gap-2"
category="insight-ftux"
category={cta.category}
label={cta.trackingLabel}
>
{cta.label}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CodeServer } from "@/components/ui/code/code.server";
import { isProd } from "@/constants/env";
import { ClientIDSection } from "../components/ProjectFTUX/ClientIDSection";
import { WaitingForIntegrationCard } from "../components/WaitingForIntegrationCard/WaitingForIntegrationCard";

export function InsightFTUX(props: {
Expand All @@ -8,7 +9,6 @@ export function InsightFTUX(props: {
return (
<WaitingForIntegrationCard
title="Integrate Insight"
clientId={props.clientId}
codeTabs={[
{
label: "JavaScript",
Expand Down Expand Up @@ -46,14 +46,19 @@ export function InsightFTUX(props: {
label: "Try on Playground",
href: "https://playground.thirdweb.com/insight",
trackingLabel: "playground",
category: "insight-ftux",
},
{
label: "View Docs",
href: "https://portal.thirdweb.com/insight",
trackingLabel: "docs",
category: "insight-ftux",
},
]}
/>
>
<ClientIDSection clientId={props.clientId} />
<div className="h-4" />
</WaitingForIntegrationCard>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { CodeServer } from "@/components/ui/code/code.server";
import { SecretKeySection } from "../components/ProjectFTUX/SecretKeySection";
import { WaitingForIntegrationCard } from "../components/WaitingForIntegrationCard/WaitingForIntegrationCard";

export function NebulaFTUX(props: {
secretKeyMasked: string;
projectId: string;
}) {
return (
<WaitingForIntegrationCard
title="Integrate Nebula"
codeTabs={[
{
label: "JavaScript",
code: (
<CodeServer code={jsCode} className="bg-background" lang="ts" />
),
},
{
label: "Python",
code: (
<CodeServer
code={pythonCode}
className="bg-background"
lang="python"
/>
),
},
{
label: "Curl",
code: (
<CodeServer code={curlCode} className="bg-background" lang="bash" />
),
},
]}
ctas={[
{
label: "Try on Playground",
href: "https://nebula.thirdweb.com/",
trackingLabel: "playground",
category: "nebula-ftux",
},
{
label: "View Docs",
href: "https://portal.thirdweb.com/nebula",
trackingLabel: "docs",
category: "nebula-ftux",
},
]}
>
<SecretKeySection
secretKeyMasked={props.secretKeyMasked}
projectId={props.projectId}
/>
<div className="h-4" />
</WaitingForIntegrationCard>
);
}

const jsCode = `\
// Example: Send message to Nebula
// Replace PROJECT_SECRET_KEY with your project's full secret key

const res = await fetch("https://nebula-api.thirdweb.com/chat", {
method: "POST",
headers: {
"x-secret-key": "PROJECT_SECRET_KEY",
},
body: {
message: "Hello",
stream: false,
},
});
Comment on lines +64 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetch request body is incorrectly formatted. When using the Fetch API, the body needs to be JSON-stringified and the appropriate content type header should be included:

const res = await fetch("https://nebula-api.thirdweb.com/chat", {
  method: "POST",
  headers: {
    "x-secret-key": "PROJECT_SECRET_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    message: "Hello",
    stream: false,
  })
});

This correction should also be applied to the Python and cURL examples for consistency.

Suggested change
const res = await fetch("https://nebula-api.thirdweb.com/chat", {
method: "POST",
headers: {
"x-secret-key": "PROJECT_SECRET_KEY",
},
body: {
message: "Hello",
stream: false,
},
});
const res = await fetch("https://nebula-api.thirdweb.com/chat", {
method: "POST",
headers: {
"x-secret-key": "PROJECT_SECRET_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
message: "Hello",
stream: false,
})
});

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.


const data = await res.json();
`;

const curlCode = `\
# Example: Send message to Nebula
# Replace PROJECT_SECRET_KEY with your project's full secret key

curl -X POST https://nebula-api.thirdweb.com/chat \
-H "x-secret-key:PROJECT_SECRET_KEY" \
-d '{
"message": "Hello",
"stream": false,
}'
`;

const pythonCode = `\
# Example: Send message to Nebula
# Replace PROJECT_SECRET_KEY with your project's full secret key

import requests

response = requests.post("https://nebula-api.thirdweb.com/chat", headers={
"x-secret-key": "PROJECT_SECRET_KEY"
}, json={
"message": "Hello",
"stream": False,
})

data = response.json()
`;
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { isProjectActive } from "@/api/analytics";
import { getProject } from "@/api/projects";
import { getTeamBySlug } from "@/api/team";
import { redirect } from "next/navigation";
import { getAuthToken } from "../../../../api/lib/getAuthToken";
import { loginRedirect } from "../../../../login/loginRedirect";
import { NebulaAnalyticsPage } from "./components/analytics/nebula-analytics-page";
import { NebulaWaitListPage } from "./components/nebula-waitlist-page";
import { NebulaFTUX } from "./nebula-ftux";

export default async function Page(props: {
params: Promise<{
Expand Down Expand Up @@ -42,7 +44,26 @@ export default async function Page(props: {

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

const activeResponse = await isProjectActive({
teamId: team.id,
projectId: project.id,
});

const showFTUX = !activeResponse.nebula;

if (hasNebulaAccess) {
if (showFTUX) {
return (
<div>
<h1 className="mb-5 font-semibold text-3xl tracking-tight">Nebula</h1>
<NebulaFTUX
secretKeyMasked={project.secretKeys[0]?.masked || ""}
projectId={project.id}
/>
</div>
);
}

return (
<NebulaAnalyticsPage
teamId={team.id}
Expand Down
Loading