Skip to content

Commit fc062de

Browse files
committed
[Dashboard] Rename getMemberById to getMemberByAccountId and add onboarding banner
1 parent c152db3 commit fc062de

File tree

10 files changed

+80
-40
lines changed

10 files changed

+80
-40
lines changed

apps/dashboard/src/@/api/team-members.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,18 @@ export async function getMembers(teamSlug: string) {
4848
return undefined;
4949
}
5050

51-
export async function getMemberById(teamSlug: string, memberId: string) {
51+
export async function getMemberByAccountId(
52+
teamSlug: string,
53+
accountId: string,
54+
) {
5255
const token = await getAuthToken();
5356

5457
if (!token) {
5558
return undefined;
5659
}
5760

5861
const res = await fetch(
59-
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamSlug}/members/${memberId}`,
62+
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamSlug}/members/${accountId}`,
6063
{
6164
headers: {
6265
Authorization: `Bearer ${token}`,

apps/dashboard/src/app/(app)/account/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getTeams } from "@/api/team";
2-
import { getMemberById } from "@/api/team-members";
2+
import { getMemberByAccountId } from "@/api/team-members";
33
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
44
import { notFound } from "next/navigation";
55
import { getAuthToken } from "../api/lib/getAuthToken";
@@ -25,7 +25,7 @@ export default async function Page() {
2525

2626
const teamsWithRole = await Promise.all(
2727
teams.map(async (team) => {
28-
const member = await getMemberById(team.slug, account.id);
28+
const member = await getMemberByAccountId(team.slug, account.id);
2929

3030
if (!member) {
3131
notFound();

apps/dashboard/src/app/(app)/components/TeamPlanBadge.tsx

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Team } from "@/api/team";
44
import { Badge, type BadgeProps } from "@/components/ui/badge";
55
import { cn } from "@/lib/utils";
66
import { useTrack } from "hooks/analytics/useTrack";
7-
import Link from "next/link";
7+
import { useDashboardRouter } from "../../../@/lib/DashboardRouter";
88

99
const teamPlanToBadgeVariant: Record<
1010
Team["billingPlan"],
@@ -38,33 +38,28 @@ export function TeamPlanBadge(props: {
3838
className?: string;
3939
postfix?: string;
4040
}) {
41-
const badge = (
41+
const router = useDashboardRouter();
42+
const track = useTrack();
43+
44+
return (
4245
<Badge
4346
variant={teamPlanToBadgeVariant[props.plan]}
4447
className={cn("px-1.5 capitalize", props.className)}
45-
>
46-
{`${getTeamPlanBadgeLabel(props.plan)}${props.postfix || ""}`}
47-
</Badge>
48-
);
49-
50-
const track = useTrack();
51-
52-
if (props.plan === "free") {
53-
return (
54-
<Link
55-
href={`/team/${props.teamSlug}/~/settings/billing?showPlans=true`}
56-
onClick={() => {
48+
onClick={(e) => {
49+
if (props.plan === "free") {
50+
e.stopPropagation();
5751
track({
5852
category: "billing",
5953
action: "show_plans",
6054
label: "team_badge",
6155
});
62-
}}
63-
>
64-
{badge}
65-
</Link>
66-
);
67-
}
68-
69-
return badge;
56+
router.push(
57+
`/team/${props.teamSlug}/~/settings/billing?showPlans=true`,
58+
);
59+
}
60+
}}
61+
>
62+
{`${getTeamPlanBadgeLabel(props.plan)}${props.postfix || ""}`}
63+
</Badge>
64+
);
7065
}

apps/dashboard/src/app/(app)/get-started/team/[team_slug]/layout.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ import { getProjects } from "@/api/projects";
22
import { getTeamBySlug, getTeams } from "@/api/team";
33
import { AppFooter } from "@/components/blocks/app-footer";
44
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
5+
import { differenceInDays } from "date-fns";
6+
import { InfoIcon } from "lucide-react";
57
import { notFound } from "next/navigation";
8+
import {
9+
Alert,
10+
AlertDescription,
11+
AlertTitle,
12+
} from "../../../../../@/components/ui/alert";
613
import { getValidAccount } from "../../../account/settings/getAccount";
714
import {
815
getAuthToken,
@@ -32,6 +39,10 @@ export default async function Layout(props: {
3239
notFound();
3340
}
3441

42+
// show the banner only if the team was created more than 3 days ago
43+
const shouldShowOnboardingBanner =
44+
differenceInDays(new Date(), new Date(team.createdAt)) > 3;
45+
3546
// Note:
3647
// Do not check that team is already onboarded or not and redirect away from /get-started pages
3748
// because the team is marked as onboarded in the first step- instead of after completing all the steps
@@ -60,6 +71,21 @@ export default async function Layout(props: {
6071
teamsAndProjects={teamsAndProjects}
6172
/>
6273
</div>
74+
{shouldShowOnboardingBanner && (
75+
<div className="container mt-10">
76+
<Alert variant="info">
77+
<InfoIcon className="size-5" />
78+
<AlertTitle>Finish setting up your team</AlertTitle>
79+
<AlertDescription>
80+
Your team predates our latest onboarding flow, so a few steps
81+
might still be pending.
82+
<br />
83+
Completing this updated guide takes less than a minute and ensures
84+
everything is set up correctly.
85+
</AlertDescription>
86+
</Alert>
87+
</div>
88+
)}
6389
{props.children}
6490
<AppFooter />
6591
</div>
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
import type { Team } from "@/api/team";
2+
import { getMemberByAccountId } from "@/api/team-members";
23
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
4+
import { getValidAccount } from "../../account/settings/getAccount";
35

46
export function isAccountOnboardingComplete(account: Account) {
57
// if email is confirmed, onboarding is considered complete
68
return !!account.emailConfirmedAt;
79
}
810

9-
export function isTeamOnboardingComplete(team: Team) {
10-
return team.isOnboarded;
11+
export async function hasToCompleteTeamOnboarding(
12+
team: Team,
13+
pagePath: string,
14+
) {
15+
const account = await getValidAccount(pagePath);
16+
const teamMember = await getMemberByAccountId(team.slug, account.id);
17+
18+
// if we can't find the team member we cannot complete onboarding
19+
if (!teamMember || teamMember.role !== "OWNER") {
20+
return false;
21+
}
22+
23+
return !team.isOnboarded;
1124
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getStripeBalance } from "@/actions/stripe-actions";
22
import { type Team, getTeamBySlug } from "@/api/team";
3-
import { getMemberById } from "@/api/team-members";
3+
import { getMemberByAccountId } from "@/api/team-members";
44
import { getTeamSubscriptions } from "@/api/team-subscription";
55
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
66
import { redirect } from "next/navigation";
@@ -31,7 +31,7 @@ export default async function Page(props: {
3131
const [team, authToken, teamMember] = await Promise.all([
3232
getTeamBySlug(params.team_slug),
3333
getAuthToken(),
34-
getMemberById(params.team_slug, account.id),
34+
getMemberByAccountId(params.team_slug, account.id),
3535
]);
3636

3737
if (!team || !teamMember) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getTeamInvoices } from "@/actions/stripe-actions";
22
import { getTeamBySlug } from "@/api/team";
3-
import { getMemberById } from "@/api/team-members";
3+
import { getMemberByAccountId } from "@/api/team-members";
44
import { redirect } from "next/navigation";
55
import type { SearchParams } from "nuqs/server";
66
import { getValidAccount } from "../../../../../../account/settings/getAccount";
@@ -25,7 +25,7 @@ export default async function Page(props: {
2525

2626
const [team, teamMember] = await Promise.all([
2727
getTeamBySlug(params.team_slug),
28-
getMemberById(params.team_slug, account.id),
28+
getMemberByAccountId(params.team_slug, account.id),
2929
]);
3030

3131
if (!team) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getTeamBySlug } from "@/api/team";
2-
import { getMemberById } from "@/api/team-members";
2+
import { getMemberByAccountId } from "@/api/team-members";
33
import { checkDomainVerification } from "@/api/verified-domain";
44
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
55
import { notFound } from "next/navigation";
@@ -19,7 +19,7 @@ export default async function Page(props: {
1919

2020
const [team, teamMember, token, initialVerification] = await Promise.all([
2121
getTeamBySlug(params.team_slug),
22-
getMemberById(params.team_slug, account.id),
22+
getMemberByAccountId(params.team_slug, account.id),
2323
getAuthToken(),
2424
checkDomainVerification(params.team_slug),
2525
]);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getProject } from "@/api/projects";
22
import { getTeams } from "@/api/team";
3-
import { getMemberById } from "@/api/team-members";
3+
import { getMemberByAccountId } from "@/api/team-members";
44
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
55
import { notFound, redirect } from "next/navigation";
66
import { getValidAccount } from "../../../../../account/settings/getAccount";
@@ -37,7 +37,7 @@ export default async function Page(props: {
3737

3838
const teamsWithRole = await Promise.all(
3939
teams.map(async (team) => {
40-
const member = await getMemberById(team.slug, account.id);
40+
const member = await getMemberByAccountId(team.slug, account.id);
4141

4242
if (!member) {
4343
notFound();

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
44
import { Suspense } from "react";
55
import { getAuthToken } from "../../api/lib/getAuthToken";
66
import { EnsureValidConnectedWalletLoginServer } from "../../components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer";
7-
import { isTeamOnboardingComplete } from "../../login/onboarding/isOnboardingRequired";
7+
import { hasToCompleteTeamOnboarding } from "../../login/onboarding/isOnboardingRequired";
88
import { SaveLastVisitedTeamPage } from "../components/last-visited-page/SaveLastVisitedPage";
99
import {
1010
PastDueBanner,
@@ -16,8 +16,11 @@ export default async function RootTeamLayout(props: {
1616
params: Promise<{ team_slug: string }>;
1717
}) {
1818
const { team_slug } = await props.params;
19-
const authToken = await getAuthToken();
20-
const team = await getTeamBySlug(team_slug).catch(() => null);
19+
20+
const [authToken, team] = await Promise.all([
21+
getAuthToken(),
22+
getTeamBySlug(team_slug).catch(() => null),
23+
]);
2124

2225
if (!team) {
2326
redirect("/team");
@@ -27,7 +30,7 @@ export default async function RootTeamLayout(props: {
2730
redirect("/login");
2831
}
2932

30-
if (!isTeamOnboardingComplete(team)) {
33+
if (await hasToCompleteTeamOnboarding(team, `/team/${team_slug}`)) {
3134
redirect(`/get-started/team/${team.slug}`);
3235
}
3336

0 commit comments

Comments
 (0)