Skip to content

Commit ebb0e52

Browse files
committed
[TOOL-3683] Dashboard: show FTUX in team overview page when no projects are created
1 parent a1141f3 commit ebb0e52

File tree

14 files changed

+691
-392
lines changed

14 files changed

+691
-392
lines changed

apps/dashboard/src/app/account/components/AccountHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function AccountHeader(props: {
7777
isOpen: false,
7878
})
7979
}
80-
onCreateAndComplete={() => {
80+
onCreate={() => {
8181
// refresh projects
8282
router.refresh();
8383
}}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { getTeamBySlug } from "@/api/team";
2+
import { notFound } from "next/navigation";
3+
import { CreateProjectPage } from "../../../../login/onboarding/create-project-onboarding/create-project-page";
4+
5+
export default async function Page(props: {
6+
params: Promise<{ team_slug: string }>;
7+
}) {
8+
const params = await props.params;
9+
const team = await getTeamBySlug(params.team_slug);
10+
11+
if (!team) {
12+
notFound();
13+
}
14+
15+
return (
16+
<CreateProjectPage
17+
enableNebulaServiceByDefault={team.enabledScopes.includes("nebula")}
18+
teamSlug={team.slug}
19+
teamId={team.id}
20+
/>
21+
);
22+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { projectStub } from "../../../../stories/stubs";
3+
import { CreateProjectPageUI } from "./CreateProjectPageUI";
4+
5+
const meta = {
6+
title: "Onboarding/CreateProject",
7+
component: Story,
8+
parameters: {
9+
nextjs: {
10+
appDirectory: true,
11+
},
12+
},
13+
} satisfies Meta<typeof Story>;
14+
15+
export default meta;
16+
type Story = StoryObj<typeof meta>;
17+
18+
export const Variants: Story = {
19+
args: {},
20+
};
21+
22+
function Story() {
23+
return (
24+
<CreateProjectPageUI
25+
createProject={async () => {
26+
await new Promise((resolve) => setTimeout(resolve, 1000));
27+
return {
28+
project: projectStub("foo", "bar"),
29+
secret: "secret",
30+
};
31+
}}
32+
teamSlug="foo"
33+
enableNebulaServiceByDefault={false}
34+
/>
35+
);
36+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"use client";
2+
3+
import type { Project } from "@/api/projects";
4+
import { useState } from "react";
5+
import {
6+
CreateProjectForm,
7+
CreatedProjectDetails,
8+
} from "../../../../components/settings/ApiKeys/Create";
9+
import { CreateProjectOnboardingLayout } from "../onboarding-layout";
10+
11+
export function CreateProjectPageUI(props: {
12+
createProject: (param: Partial<Project>) => Promise<{
13+
project: Project;
14+
secret: string;
15+
}>;
16+
enableNebulaServiceByDefault: boolean;
17+
teamSlug: string;
18+
}) {
19+
const [screen, setScreen] = useState<
20+
{ id: "create" } | { id: "api-details"; project: Project; secret: string }
21+
>({ id: "create" });
22+
return (
23+
<CreateProjectOnboardingLayout currentStep={screen.id === "create" ? 1 : 2}>
24+
<div className="overflow-hidden rounded-lg border bg-card">
25+
{screen.id === "create" && (
26+
<CreateProjectForm
27+
showTitle={false}
28+
closeModal={undefined}
29+
createProject={props.createProject}
30+
onProjectCreated={(params) => {
31+
setScreen({
32+
id: "api-details",
33+
project: params.project,
34+
secret: params.secret,
35+
});
36+
}}
37+
enableNebulaServiceByDefault={props.enableNebulaServiceByDefault}
38+
/>
39+
)}
40+
41+
{screen.id === "api-details" && (
42+
<CreatedProjectDetails
43+
project={screen.project}
44+
secret={screen.secret}
45+
teamSlug={props.teamSlug}
46+
/>
47+
)}
48+
</div>
49+
</CreateProjectOnboardingLayout>
50+
);
51+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use client";
2+
3+
import { createProjectClient } from "@3rdweb-sdk/react/hooks/useApi";
4+
import { CreateProjectPageUI } from "./CreateProjectPageUI";
5+
6+
export function CreateProjectPage(props: {
7+
enableNebulaServiceByDefault: boolean;
8+
teamSlug: string;
9+
teamId: string;
10+
}) {
11+
return (
12+
<CreateProjectPageUI
13+
enableNebulaServiceByDefault={props.enableNebulaServiceByDefault}
14+
teamSlug={props.teamSlug}
15+
createProject={async (params) => {
16+
const res = await createProjectClient(props.teamId, params);
17+
return {
18+
project: res.project,
19+
secret: res.secret,
20+
};
21+
}}
22+
/>
23+
);
24+
}

apps/dashboard/src/app/login/onboarding/onboarding-layout.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { cn } from "@/lib/utils";
44
import { useMutation } from "@tanstack/react-query";
55
import {
66
BoxIcon,
7+
KeyRoundIcon,
78
LogOutIcon,
89
MailIcon,
910
UserIcon,
@@ -99,6 +100,36 @@ export function TeamOnboardingLayout(props: {
99100
);
100101
}
101102

103+
const createProjectOnboardingSteps: OnboardingStep[] = [
104+
{
105+
icon: BoxIcon,
106+
title: "Configure Project",
107+
description: "Provide project details",
108+
number: 1,
109+
},
110+
{
111+
icon: KeyRoundIcon,
112+
title: "Store Secret Key",
113+
description: "Store your secret key",
114+
number: 2,
115+
},
116+
];
117+
118+
export function CreateProjectOnboardingLayout(props: {
119+
children: React.ReactNode;
120+
currentStep: 1 | 2;
121+
}) {
122+
return (
123+
<OnboardingLayout
124+
steps={createProjectOnboardingSteps}
125+
currentStep={props.currentStep}
126+
title="Create a project"
127+
>
128+
{props.children}
129+
</OnboardingLayout>
130+
);
131+
}
132+
102133
function OnboardingLayout(props: {
103134
steps: OnboardingStep[];
104135
currentStep: number;

apps/dashboard/src/app/login/onboarding/team-onboarding/team-onboarding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function InviteTeamMembers(props: {
104104
<InviteTeamMembersUI
105105
trackEvent={trackEvent}
106106
onComplete={() => {
107-
router.replace(`/team/${props.team.slug}`);
107+
router.replace(`/get-started/team/${props.team.slug}/create-project`);
108108
}}
109109
getTeam={async () => {
110110
const res = await apiServerProxy<{

apps/dashboard/src/app/team/[team_slug]/(team)/page.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import { subDays } from "date-fns";
66
import { redirect } from "next/navigation";
77
import { getAuthToken } from "../../../api/lib/getAuthToken";
88
import { loginRedirect } from "../../../login/loginRedirect";
9-
import {
10-
type ProjectWithAnalytics,
11-
TeamProjectsPage,
12-
} from "./~/projects/TeamProjectsPage";
9+
import { TeamProjectsPage } from "./~/projects/TeamProjectsPage";
10+
import type { ProjectWithAnalytics } from "./~/projects/TeamProjectsTable";
1311

1412
export default async function Page(props: {
1513
params: Promise<{ team_slug: string }>;

0 commit comments

Comments
 (0)