Skip to content

Commit 9138778

Browse files
thirdweb AI inside dashboard
1 parent e37bd8e commit 9138778

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+6519
-97
lines changed

apps/dashboard/src/@/components/chat/ChatBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export function ChatBar(props: {
8787
<PopoverContent className="w-72">
8888
<div>
8989
<p className="mb-3 text-muted-foreground text-sm">
90-
Get access to image uploads by signing in to Nebula
90+
Get access to image uploads by signing in to thirdweb
9191
</p>
9292
<Button
9393
className="w-full"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"use client";
2+
3+
import type React from "react";
4+
import { useRef } from "react";
5+
import { Button } from "@/components/ui/button";
6+
7+
interface ImageUploadProps {
8+
value: File | undefined;
9+
onChange?: (files: File[]) => void;
10+
children?: React.ReactNode;
11+
variant?: React.ComponentProps<typeof Button>["variant"];
12+
className?: string;
13+
multiple?: boolean;
14+
accept: string;
15+
}
16+
17+
export function ImageUploadButton(props: ImageUploadProps) {
18+
const fileInputRef = useRef<HTMLInputElement>(null);
19+
20+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
21+
const files = Array.from(e.target.files || []);
22+
props.onChange?.(files);
23+
};
24+
25+
return (
26+
<div>
27+
<Button
28+
className={props.className}
29+
onClick={() => fileInputRef.current?.click()}
30+
variant={props.variant}
31+
>
32+
{props.children}
33+
</Button>
34+
<input
35+
accept={props.accept}
36+
aria-label="Upload image"
37+
className="hidden"
38+
multiple={props.multiple}
39+
onChange={handleFileChange}
40+
ref={fileInputRef}
41+
type="file"
42+
/>
43+
</div>
44+
);
45+
}

apps/dashboard/src/@/constants/public-envs.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ export const NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET =
2929

3030
export const NEXT_PUBLIC_DEMO_ENGINE_URL =
3131
process.env.NEXT_PUBLIC_DEMO_ENGINE_URL || "";
32+
33+
export const NEXT_PUBLIC_THIRDWEB_AI_HOST =
34+
process.env.NEXT_PUBLIC_THIRDWEB_AI_HOST || "https://nebula-api.thirdweb.com";

apps/dashboard/src/@/storybook/stubs.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,32 @@ export function newAccountStub(overrides?: Partial<Account>): Account {
279279
...overrides,
280280
};
281281
}
282+
283+
export function randomLorem(length: number) {
284+
const loremWords = [
285+
"lorem",
286+
"ipsum",
287+
"dolor",
288+
"sit",
289+
"amet",
290+
"consectetur",
291+
"adipiscing",
292+
"elit",
293+
"sed",
294+
"do",
295+
"eiusmod",
296+
"tempor",
297+
"incididunt",
298+
"ut",
299+
"labore",
300+
"et",
301+
"dolore",
302+
"magna",
303+
"aliqua",
304+
];
305+
306+
return Array.from({ length }, () => {
307+
const randomIndex = Math.floor(Math.random() * loremWords.length);
308+
return loremWords[randomIndex];
309+
}).join(" ");
310+
}

apps/dashboard/src/app/(app)/(dashboard)/(chain)/components/server/products.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ export const products = [
6262
description: "The most powerful AI for interacting with the blockchain",
6363
icon: NebulaIcon,
6464
id: "nebula",
65-
link: "https://thirdweb.com/nebula",
66-
name: "Nebula",
65+
link: "https://thirdweb.com/ai",
66+
name: "thirdweb AI",
6767
},
6868
] satisfies Array<{
6969
name: string;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { redirect } from "next/navigation";
2+
import { ResponsiveSearchParamsProvider } from "responsive-rsc";
3+
import { getAuthToken } from "@/api/auth-token";
4+
import { getProject } from "@/api/project/projects";
5+
import type { DurationId } from "@/components/analytics/date-range-selector";
6+
import { ResponsiveTimeFilters } from "@/components/analytics/responsive-time-filters";
7+
import { ProjectPage } from "@/components/blocks/project-page/project-page";
8+
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
9+
import { NebulaIcon } from "@/icons/NebulaIcon";
10+
import { getFiltersFromSearchParams } from "@/lib/time";
11+
import { loginRedirect } from "@/utils/redirects";
12+
import { AiAnalytics } from "./chart";
13+
import { AiSummary } from "./chart/Summary";
14+
15+
export default async function Page(props: {
16+
params: Promise<{ team_slug: string; project_slug: string }>;
17+
searchParams: Promise<{
18+
from?: string;
19+
to?: string;
20+
type?: string;
21+
interval?: string;
22+
}>;
23+
}) {
24+
const [searchParams, params] = await Promise.all([
25+
props.searchParams,
26+
props.params,
27+
]);
28+
29+
const { team_slug, project_slug } = params;
30+
31+
const [project, authToken] = await Promise.all([
32+
getProject(team_slug, project_slug),
33+
getAuthToken(),
34+
]);
35+
36+
if (!authToken) {
37+
loginRedirect(`/team/${team_slug}/${project_slug}/ai`);
38+
}
39+
40+
if (!project) {
41+
redirect(`/team/${team_slug}`);
42+
}
43+
44+
const client = getClientThirdwebClient({
45+
jwt: authToken,
46+
teamId: project.teamId,
47+
});
48+
49+
const defaultRange = "last-30" as DurationId;
50+
const { range, interval } = getFiltersFromSearchParams({
51+
defaultRange,
52+
from: searchParams.from,
53+
interval: searchParams.interval,
54+
to: searchParams.to,
55+
});
56+
57+
return (
58+
<ResponsiveSearchParamsProvider value={searchParams}>
59+
<ProjectPage
60+
header={{
61+
icon: NebulaIcon,
62+
client,
63+
title: "AI Analytics",
64+
description: "Track your AI app usage and performance",
65+
actions: null,
66+
links: [
67+
{
68+
href: "https://portal.thirdweb.com/ai/chat",
69+
type: "docs",
70+
},
71+
{
72+
href: "https://api.thirdweb.com/reference#tag/ai/post/ai/chat",
73+
type: "api",
74+
},
75+
{
76+
href: "https://playground.thirdweb.com/ai/chat",
77+
type: "playground",
78+
},
79+
],
80+
}}
81+
>
82+
<div className="flex flex-col gap-4 md:gap-6">
83+
<ResponsiveTimeFilters defaultRange={defaultRange} />
84+
<AiSummary
85+
projectId={project.id}
86+
teamId={project.teamId}
87+
authToken={authToken}
88+
range={range}
89+
/>
90+
91+
<AiAnalytics
92+
interval={interval}
93+
projectId={project.id}
94+
range={range}
95+
teamId={project.teamId}
96+
authToken={authToken}
97+
/>
98+
</div>
99+
</ProjectPage>
100+
</ResponsiveSearchParamsProvider>
101+
);
102+
}

0 commit comments

Comments
 (0)