Skip to content

Commit 0732ddd

Browse files
committed
[TOOL-3446] Dashboard: Revamp account onboarding, Add Team onboarding
1 parent 3f6d525 commit 0732ddd

File tree

42 files changed

+2237
-1061
lines changed

Some content is hidden

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

42 files changed

+2237
-1061
lines changed

.changeset/chilly-trams-wash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/service-utils": patch
3+
---
4+
5+
Update `TeamResponse` type

apps/dashboard/.storybook/preview.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Inter as interFont } from "next/font/google";
77
// biome-ignore lint/style/useImportType: <explanation>
88
import React from "react";
99
import { useEffect } from "react";
10+
import { Toaster } from "sonner";
1011
import { Button } from "../src/@/components/ui/button";
1112

1213
const queryClient = new QueryClient();
@@ -16,8 +17,30 @@ const fontSans = interFont({
1617
variable: "--font-sans",
1718
});
1819

20+
const customViewports = {
21+
xs: {
22+
// Regular sized phones (iphone 15 / 15 pro)
23+
name: "iPhone",
24+
styles: {
25+
width: "390px",
26+
height: "844px",
27+
},
28+
},
29+
sm: {
30+
// Larger phones (iphone 15 plus / 15 pro max)
31+
name: "iPhone Plus",
32+
styles: {
33+
width: "430px",
34+
height: "932px",
35+
},
36+
},
37+
};
38+
1939
const preview: Preview = {
2040
parameters: {
41+
viewport: {
42+
viewports: customViewports,
43+
},
2144
controls: {
2245
matchers: {
2346
color: /(background|color)$/i,
@@ -57,13 +80,13 @@ function StoryLayout(props: {
5780

5881
return (
5982
<QueryClientProvider client={queryClient}>
60-
<div className="flex min-h-screen min-w-0 flex-col bg-background text-foreground">
83+
<div className="flex min-h-dvh min-w-0 flex-col bg-background text-foreground">
6184
<div className="flex justify-end gap-2 border-b p-4">
6285
<Button
6386
onClick={() => setTheme("dark")}
6487
size="sm"
6588
variant={theme === "dark" ? "default" : "outline"}
66-
className="h-auto w-auto rounded-full p-2"
89+
className="h-auto w-auto shrink-0 rounded-full p-2"
6790
>
6891
<MoonIcon className="size-4" />
6992
</Button>
@@ -72,14 +95,20 @@ function StoryLayout(props: {
7295
onClick={() => setTheme("light")}
7396
size="sm"
7497
variant={theme === "light" ? "default" : "outline"}
75-
className="h-auto w-auto rounded-full p-2"
98+
className="h-auto w-auto shrink-0 rounded-full p-2"
7699
>
77100
<SunIcon className="size-4" />
78101
</Button>
79102
</div>
80103

81104
<div className="flex min-w-0 grow flex-col">{props.children}</div>
105+
<ToasterSetup />
82106
</div>
83107
</QueryClientProvider>
84108
);
85109
}
110+
111+
function ToasterSetup() {
112+
const { theme } = useTheme();
113+
return <Toaster richColors theme={theme === "light" ? "light" : "dark"} />;
114+
}

apps/dashboard/src/@/components/blocks/pricing-card.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ type PricingCardProps = {
3131
ctaHint?: string;
3232
highlighted?: boolean;
3333
current?: boolean;
34-
canTrialGrowth?: boolean;
3534
activeTrialEndsAt?: string;
3635
redirectPath: string;
3736
redirectToCheckout: RedirectBillingCheckoutAction;
@@ -43,7 +42,6 @@ export const PricingCard: React.FC<PricingCardProps> = ({
4342
cta,
4443
highlighted = false,
4544
current = false,
46-
canTrialGrowth = false,
4745
activeTrialEndsAt,
4846
redirectPath,
4947
redirectToCheckout,
@@ -88,18 +86,7 @@ export const PricingCard: React.FC<PricingCardProps> = ({
8886
<div className="flex flex-col gap-0.5">
8987
<div className="flex items-center gap-2">
9088
<span className="font-semibold text-3xl text-foreground tracking-tight">
91-
{isCustomPrice ? (
92-
plan.price
93-
) : canTrialGrowth ? (
94-
<>
95-
<span className="text-muted-foreground line-through">
96-
${plan.price}
97-
</span>{" "}
98-
$0
99-
</>
100-
) : (
101-
`$${plan.price}`
102-
)}
89+
${plan.price}
10390
</span>
10491

10592
{!isCustomPrice && (

apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts

Lines changed: 72 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,6 @@ export type Account = {
4444
// TODO - add image URL
4545
};
4646

47-
interface UpdateAccountInput {
48-
name?: string;
49-
email?: string;
50-
linkWallet?: boolean;
51-
subscribeToUpdates?: boolean;
52-
onboardSkipped?: boolean;
53-
}
54-
5547
interface UpdateAccountNotificationsInput {
5648
billing: "email" | "none";
5749
updates: "email" | "none";
@@ -140,45 +132,40 @@ export function useAccountCredits() {
140132
});
141133
}
142134

143-
export function useUpdateAccount() {
144-
const queryClient = useQueryClient();
145-
const address = useActiveAccount()?.address;
146-
147-
return useMutation({
148-
mutationFn: async (input: UpdateAccountInput) => {
149-
type Result = {
150-
data: object;
151-
error?: { message: string };
152-
};
135+
export type UpdateAccountParams = {
136+
name?: string;
137+
email?: string;
138+
linkWallet?: boolean;
139+
subscribeToUpdates?: boolean;
140+
onboardSkipped?: boolean;
141+
};
153142

154-
const res = await apiServerProxy<Result>({
155-
pathname: "/v1/account",
156-
method: "PUT",
157-
headers: {
158-
"Content-Type": "application/json",
159-
},
160-
body: JSON.stringify(input),
161-
});
143+
export async function updateAccountClient(input: UpdateAccountParams) {
144+
type Result = {
145+
data: object;
146+
error?: { message: string };
147+
};
162148

163-
if (!res.ok) {
164-
throw new Error(res.error);
165-
}
149+
const res = await apiServerProxy<Result>({
150+
pathname: "/v1/account",
151+
method: "PUT",
152+
headers: {
153+
"Content-Type": "application/json",
154+
},
155+
body: JSON.stringify(input),
156+
});
166157

167-
const json = res.data;
158+
if (!res.ok) {
159+
throw new Error(res.error);
160+
}
168161

169-
if (json.error) {
170-
throw new Error(json.error.message);
171-
}
162+
const json = res.data;
172163

173-
return json.data;
174-
},
164+
if (json.error) {
165+
throw new Error(json.error.message);
166+
}
175167

176-
onSuccess: () => {
177-
return queryClient.invalidateQueries({
178-
queryKey: accountKeys.me(address || ""),
179-
});
180-
},
181-
});
168+
return json.data;
182169
}
183170

184171
export function useUpdateNotifications() {
@@ -221,77 +208,61 @@ export function useUpdateNotifications() {
221208
});
222209
}
223210

224-
export function useConfirmEmail() {
225-
return useMutation({
226-
mutationFn: async (input: ConfirmEmailInput) => {
227-
type Result = {
228-
error?: { message: string };
229-
data: { team: Team; account: Account };
230-
};
231-
232-
const res = await apiServerProxy<Result>({
233-
pathname: "/v1/account/confirmEmail",
234-
method: "PUT",
235-
headers: {
236-
"Content-Type": "application/json",
237-
},
238-
body: JSON.stringify(input),
239-
});
240-
241-
if (!res.ok) {
242-
throw new Error(res.error);
243-
}
244-
245-
const json = res.data;
246-
247-
if (json.error) {
248-
throw new Error(json.error.message);
249-
}
211+
export const verifyEmailClient = async (input: ConfirmEmailInput) => {
212+
type Result = {
213+
error?: { message: string };
214+
data: { team: Team; account: Account };
215+
};
250216

251-
return json.data;
217+
const res = await apiServerProxy<Result>({
218+
pathname: "/v1/account/confirmEmail",
219+
method: "PUT",
220+
headers: {
221+
"Content-Type": "application/json",
252222
},
223+
body: JSON.stringify(input),
253224
});
254-
}
255225

256-
export function useResendEmailConfirmation() {
257-
const address = useActiveAccount()?.address;
258-
const queryClient = useQueryClient();
226+
if (!res.ok) {
227+
throw new Error(res.error);
228+
}
259229

260-
return useMutation({
261-
mutationFn: async () => {
262-
type Result = {
263-
error?: { message: string };
264-
data: object;
265-
};
230+
const json = res.data;
266231

267-
const res = await apiServerProxy<Result>({
268-
pathname: "/v1/account/resendEmailConfirmation",
269-
method: "POST",
270-
headers: {
271-
"Content-Type": "application/json",
272-
},
273-
body: JSON.stringify({}),
274-
});
275-
276-
if (!res.ok) {
277-
throw new Error(res.error);
278-
}
232+
if (json.error) {
233+
throw new Error(json.error.message);
234+
}
279235

280-
const json = res.data;
236+
return json.data;
237+
};
281238

282-
if (json.error) {
283-
throw new Error(json.error.message);
284-
}
239+
export const resendEmailClient = async () => {
240+
type Result = {
241+
error?: { message: string };
242+
data: object;
243+
};
285244

286-
return json.data;
287-
},
288-
onSuccess: () => {
289-
return queryClient.invalidateQueries({
290-
queryKey: accountKeys.me(address || ""),
291-
});
245+
const res = await apiServerProxy<Result>({
246+
pathname: "/v1/account/resendEmailConfirmation",
247+
method: "POST",
248+
headers: {
249+
"Content-Type": "application/json",
292250
},
251+
body: JSON.stringify({}),
293252
});
294-
}
253+
254+
if (!res.ok) {
255+
throw new Error(res.error);
256+
}
257+
258+
const json = res.data;
259+
260+
if (json.error) {
261+
throw new Error(json.error.message);
262+
}
263+
264+
return json.data;
265+
};
295266

296267
export async function createProjectClient(
297268
teamId: string,

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import {
5252
useWalletBalance,
5353
} from "thirdweb/react";
5454
import { z } from "zod";
55-
import { isOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired";
55+
import { isAccountOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired";
5656

5757
function formatTime(seconds: number) {
5858
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
@@ -210,7 +210,7 @@ export function FaucetButton({
210210
);
211211
}
212212

213-
if (!isOnboardingComplete(twAccount)) {
213+
if (!isAccountOnboardingComplete(twAccount)) {
214214
return (
215215
<Button asChild className="w-full">
216216
<Link

apps/dashboard/src/app/account/settings/getAccount.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { API_SERVER_URL } from "@/constants/env";
22
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
33
import { getAuthToken } from "../../api/lib/getAuthToken";
44
import { loginRedirect } from "../../login/loginRedirect";
5-
import { isOnboardingComplete } from "../../login/onboarding/isOnboardingRequired";
5+
import { isAccountOnboardingComplete } from "../../login/onboarding/isOnboardingRequired";
66

77
/**
88
* Just get the account object without enforcing onboarding.
@@ -40,7 +40,7 @@ export async function getValidAccount(pagePath?: string) {
4040
const account = await getRawAccount();
4141

4242
// enforce login & onboarding
43-
if (!account || !isOnboardingComplete(account)) {
43+
if (!account || !isAccountOnboardingComplete(account)) {
4444
loginRedirect(pagePath);
4545
}
4646

0 commit comments

Comments
 (0)