Skip to content

Commit dbb7824

Browse files
committed
[TOOL-4291] Dashboard: Show recommended team members in invite section (#6867)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the team onboarding and member management features by introducing a `client` prop for API interactions, implementing recommended members functionality, and updating various components to utilize this new feature effectively. ### Detailed summary - Added `client` prop to `InviteTeamMembers` and related components. - Implemented `getRecommendedMembers` function to fetch recommended team members. - Updated `TeamMembersSettingsPage` to display recommended members. - Enhanced `InviteSection` to handle recommended members. - Modified story files to include new client interactions and recommended members. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 692c25e commit dbb7824

File tree

12 files changed

+283
-9
lines changed

12 files changed

+283
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import { getTeamBySlug } from "@/api/team";
2+
import { getThirdwebClient } from "@/constants/thirdweb.server";
23
import { notFound } from "next/navigation";
4+
import { getAuthToken } from "../../../../api/lib/getAuthToken";
35
import { TeamOnboardingLayout } from "../../../../login/onboarding/onboarding-layout";
46
import { InviteTeamMembers } from "../../../../login/onboarding/team-onboarding/team-onboarding";
57

68
export default async function Page(props: {
79
params: Promise<{ team_slug: string }>;
810
}) {
911
const params = await props.params;
10-
const team = await getTeamBySlug(params.team_slug);
12+
const [team, authToken] = await Promise.all([
13+
getTeamBySlug(params.team_slug),
14+
getAuthToken(),
15+
]);
1116

12-
if (!team) {
17+
if (!team || !authToken) {
1318
notFound();
1419
}
1520

21+
const client = getThirdwebClient({
22+
jwt: authToken,
23+
teamId: team.id,
24+
});
25+
1626
return (
1727
<TeamOnboardingLayout currentStep={2}>
18-
<InviteTeamMembers team={team} />
28+
<InviteTeamMembers team={team} client={client} />
1929
</TeamOnboardingLayout>
2030
);
2131
}

apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.stories.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Team } from "@/api/team";
22
import type { Meta, StoryObj } from "@storybook/react";
33
import { teamStub } from "stories/stubs";
4-
import { storybookLog } from "stories/utils";
4+
import { storybookLog, storybookThirdwebClient } from "stories/utils";
55
import { TeamOnboardingLayout } from "../onboarding-layout";
66
import { InviteTeamMembersUI } from "./InviteTeamMembers";
77

@@ -61,6 +61,7 @@ function Story(props: {
6161
return (
6262
<TeamOnboardingLayout currentStep={2}>
6363
<InviteTeamMembersUI
64+
client={storybookThirdwebClient}
6465
trackEvent={(params) => {
6566
storybookLog("trackEvent", params);
6667
}}

apps/dashboard/src/app/(app)/login/onboarding/team-onboarding/InviteTeamMembers.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useDashboardRouter } from "@/lib/DashboardRouter";
1818
import type { TrackingParams } from "hooks/analytics/useTrack";
1919
import { ArrowRightIcon, CircleArrowUpIcon } from "lucide-react";
2020
import { useState, useTransition } from "react";
21+
import type { ThirdwebClient } from "thirdweb";
2122
import { pollWithTimeout } from "utils/pollWithTimeout";
2223
import { useStripeRedirectEvent } from "../../../stripe-redirect/stripeRedirectChannel";
2324
import {
@@ -32,6 +33,7 @@ export function InviteTeamMembersUI(props: {
3233
onComplete: () => void;
3334
getTeam: () => Promise<Team>;
3435
trackEvent: (params: TrackingParams) => void;
36+
client: ThirdwebClient;
3537
}) {
3638
const [showPlanModal, setShowPlanModal] = useState(false);
3739
const [isPending, startTransition] = useTransition();
@@ -93,6 +95,9 @@ export function InviteTeamMembersUI(props: {
9395
userHasEditPermission={true}
9496
onInviteSuccess={() => setHasSentInvites(true)}
9597
shouldHideInviteButton={hasSentInvites}
98+
client={props.client}
99+
// its a new team, there's no recommended members
100+
recommendedMembers={[]}
96101
customCTASection={
97102
<div className="flex gap-3">
98103
{props.team.billingPlan === "free" && (

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

+2
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,15 @@ export function TeamInfoForm(props: {
9696

9797
export function InviteTeamMembers(props: {
9898
team: Team;
99+
client: ThirdwebClient;
99100
}) {
100101
const router = useDashboardRouter();
101102
const trackEvent = useTrack();
102103

103104
return (
104105
<InviteTeamMembersUI
105106
trackEvent={trackEvent}
107+
client={props.client}
106108
onComplete={() => {
107109
router.replace(`/team/${props.team.slug}`);
108110
}}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/add-partner/page.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { getTeamBySlug } from "@/api/team";
2-
import {} from "@/components/ui/breadcrumb";
32
import { notFound } from "next/navigation";
43
import { getAuthToken } from "../../../../../../../../../api/lib/getAuthToken";
54
import { loginRedirect } from "../../../../../../../../../login/loginRedirect";

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/partners/[partner_id]/edit/page.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { getTeamBySlug } from "@/api/team";
2-
import {} from "@/components/ui/breadcrumb";
32
import { notFound } from "next/navigation";
43
import { getAuthToken } from "../../../../../../../../../../../api/lib/getAuthToken";
54
import { loginRedirect } from "../../../../../../../../../../../login/loginRedirect";

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/members/InviteSection.stories.tsx

+45
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
44
import type { Meta, StoryObj } from "@storybook/react";
55
import { useState } from "react";
66
import { teamStub } from "stories/stubs";
7+
import { storybookThirdwebClient } from "../../../../../../../../stories/utils";
78
import { InviteSection } from "./InviteSection";
89

910
const meta = {
@@ -36,6 +37,14 @@ const TEAM_CONFIGS = [
3637
{ id: "pro", label: "Pro Team", team: teamStub("bar", "pro") },
3738
] as const;
3839

40+
const RECOMMENDED_MEMBERS_COUNTS = [
41+
{ id: "0", label: "No Recommended Members", value: 0 },
42+
{ id: "1", label: "1 Recommended Member", value: 1 },
43+
{ id: "3", label: "2 Recommended Members", value: 2 },
44+
{ id: "5", label: "11 Recommended Members", value: 11 },
45+
{ id: "100", label: "100 Recommended Members", value: 100 },
46+
] as const;
47+
3948
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
4049
type InviteParams = Array<{ email: string; role: TeamAccountRole }>;
4150

@@ -79,12 +88,26 @@ function Story() {
7988
const [hasEditPermission, setHasEditPermission] = useState("true");
8089
const [inviteResult, setInviteResult] =
8190
useState<keyof typeof INVITE_HANDLERS>("success");
91+
const [recommendedMembersCount, setRecommendedMembersCount] =
92+
useState<number>(0);
8293

8394
const showPermissionControls =
8495
selectedTeam.id !== "free" && selectedTeam.id !== "pro";
8596
const showInviteControls =
8697
showPermissionControls && hasEditPermission === "true";
8798

99+
const recommendedMembers = Array.from(
100+
{ length: recommendedMembersCount },
101+
(_, i) => ({
102+
email: `user${i + 1}@example.com`,
103+
name: i % 2 === 0 ? `User ${i + 1}` : null,
104+
image:
105+
i % 3 === 0
106+
? `https://api.dicebear.com/7.x/avataaars/svg?seed=${i + 1}`
107+
: null,
108+
}),
109+
);
110+
88111
return (
89112
<div className="container max-w-6xl py-10">
90113
<div className="mb-8 flex flex-col gap-6">
@@ -154,13 +177,35 @@ function Story() {
154177
</RadioGroup>
155178
</div>
156179
)}
180+
181+
<div>
182+
<h3 className="mb-3 font-medium">Recommended Members</h3>
183+
<RadioGroup
184+
value={recommendedMembersCount.toString()}
185+
onValueChange={(value) => {
186+
setRecommendedMembersCount(Number.parseInt(value));
187+
}}
188+
className="flex gap-4"
189+
>
190+
{RECOMMENDED_MEMBERS_COUNTS.map(({ id, label, value }) => (
191+
<RadioOption
192+
key={id}
193+
id={id}
194+
label={label}
195+
value={value.toString()}
196+
/>
197+
))}
198+
</RadioGroup>
199+
</div>
157200
</div>
158201

159202
<div className="flex flex-col gap-10">
160203
<InviteSection
161204
team={selectedTeam.team}
162205
userHasEditPermission={hasEditPermission === "true"}
163206
inviteTeamMembers={INVITE_HANDLERS[inviteResult]}
207+
recommendedMembers={recommendedMembers}
208+
client={storybookThirdwebClient}
164209
/>
165210
</div>
166211
</div>

0 commit comments

Comments
 (0)