Skip to content

Commit

Permalink
Plug in Team settings page UI to API
Browse files Browse the repository at this point in the history
  • Loading branch information
MananTank committed Oct 4, 2024
1 parent c935fbf commit a90df36
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 45 deletions.
8 changes: 4 additions & 4 deletions apps/dashboard/src/@/api/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ export type Team = {
slug: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
bannedAt: string | null;
// image: string; // -> TODO
deletedAt?: string;
bannedAt?: string;
image?: string;
billingPlan: "pro" | "growth" | "free";
billingStatus: "validPayment" | (string & {}); // what's the other value?
// billingEmail: string;
billingEmail: string;
// billingExternalId: string;
// billingType: "STRIPE" | ??
// billingCustomerPayload: ?? | null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,9 @@ const testTeam: Team = {
slug: "team-slug-foo-bar",
createdAt: "2023-07-07T19:21:33.604Z",
updatedAt: "2024-07-11T00:01:02.241Z",
deletedAt: null,
bannedAt: null,
billingStatus: "validPayment",
billingPlan: "free",
billingEmail: "[email protected]",
};

function Story() {
Expand All @@ -52,6 +51,10 @@ function Story() {
updateTeamImage={async () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
}}
updateTeamField={async (value) => {
console.log(value);
await new Promise((resolve) => setTimeout(resolve, 1000));
}}
/>
<ComponentVariantions />
<Toaster richColors />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,51 @@
"use client";

import type { Team } from "@/api/team";
import type { ThirdwebClient } from "thirdweb";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { upload } from "thirdweb/storage";
import { TeamGeneralSettingsPageUI } from "./TeamGeneralSettingsPageUI";
import { updateTeam } from "./updateTeam";

export function TeamGeneralSettingsPage(props: {
team: Team;
client: ThirdwebClient;
authToken: string;
}) {
const router = useDashboardRouter();

return (
<TeamGeneralSettingsPageUI
team={props.team}
updateTeamField={async (teamValue) => {
await updateTeam({
teamId: props.team.id,
value: teamValue,
});

// Current page's slug is updated
if (teamValue.slug) {
router.replace(`/team/${teamValue.slug}/~/settings`);
} else {
router.refresh();
}
}}
updateTeamImage={async (file) => {
let uri: string | undefined = undefined;

if (file) {
// upload to IPFS
const uri = await upload({
client: props.client,
uri = await upload({
client: getThirdwebClient(props.authToken),
files: [file],
});

// TODO - Implement updating the account image with uri
console.log(uri);
} else {
// TODO - Implement deleting the account image
}

throw new Error("Not implemented");
await updateTeam({
teamId: props.team.id,
value: {
image: uri,
},
});
}}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,37 @@ import { DangerSettingCard } from "@/components/blocks/DangerSettingCard";
import { SettingsCard } from "@/components/blocks/SettingsCard";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Input } from "@/components/ui/input";
import { useThirdwebClient } from "@/constants/thirdweb.client";
import { useDashboardRouter } from "@/lib/DashboardRouter";
import { useMutation } from "@tanstack/react-query";
import { FileInput } from "components/shared/FileInput";
import { useState } from "react";
import { toast } from "sonner";
import { resolveScheme } from "thirdweb/storage";

type UpdateTeamField = (team: Partial<Team>) => Promise<void>;

export function TeamGeneralSettingsPageUI(props: {
team: Team;
updateTeamImage: (file: File | undefined) => Promise<void>;
updateTeamField: UpdateTeamField;
}) {
const hasPermissionToDelete = false; // TODO
return (
<div className="flex flex-col gap-8">
<TeamNameFormControl team={props.team} />
<TeamSlugFormControl team={props.team} />
<TeamAvatarFormControl updateTeamImage={props.updateTeamImage} />
<TeamNameFormControl
team={props.team}
updateTeamField={props.updateTeamField}
/>
<TeamSlugFormControl
team={props.team}
updateTeamField={props.updateTeamField}
/>
{/* THIS IS NOT WORKING - CAN"T UPDATE IMAGE */}
<TeamAvatarFormControl
updateTeamImage={props.updateTeamImage}
avatar={props.team.image}
/>
<TeamIdCard team={props.team} />
<LeaveTeamCard enabled={false} teamName={props.team.name} />
<DeleteTeamCard
Expand All @@ -33,17 +48,13 @@ export function TeamGeneralSettingsPageUI(props: {

function TeamNameFormControl(props: {
team: Team;
updateTeamField: UpdateTeamField;
}) {
const [teamName, setTeamName] = useState(props.team.name);
const maxTeamNameLength = 32;

// TODO - implement
const updateTeamMutation = useMutation({
mutationFn: async (teamName: string) => {
await new Promise((resolve) => setTimeout(resolve, 3000));
console.log("Updating team name to", teamName);
throw new Error("Not implemented");
},
mutationFn: (name: string) => props.updateTeamField({ name }),
});

function handleSave() {
Expand Down Expand Up @@ -82,20 +93,14 @@ function TeamNameFormControl(props: {

function TeamSlugFormControl(props: {
team: Team;
updateTeamField: (team: Partial<Team>) => Promise<void>;
}) {
const [teamSlug, setTeamSlug] = useState(props.team.slug);
const [isTeamTaken] = useState(false);
const maxTeamURLLength = 48;

// TODO - implement
const updateTeamMutation = useMutation({
mutationFn: async (_slug: string) => {
// set isTeamTaken to true if team URL is taken
// Fake loading
await new Promise((resolve) => setTimeout(resolve, 3000));
console.log("Updating team slug to", _slug);
throw new Error("Not implemented");
},
mutationFn: (slug: string) => props.updateTeamField({ slug: slug }),
});

function handleSave() {
Expand Down Expand Up @@ -144,8 +149,17 @@ function TeamSlugFormControl(props: {

function TeamAvatarFormControl(props: {
updateTeamImage: (file: File | undefined) => Promise<void>;
avatar: string | undefined;
}) {
const [teamAvatar, setTeamAvatar] = useState<File>(); // TODO: prefill with team avatar
const client = useThirdwebClient();
const teamUrl = props.avatar
? resolveScheme({
client: client,
uri: props.avatar,
})
: undefined;

const [teamAvatar, setTeamAvatar] = useState<File | undefined>();

const updateTeamAvatarMutation = useMutation({
mutationFn: async (_avatar: File | undefined) => {
Expand Down Expand Up @@ -186,6 +200,7 @@ function TeamAvatarFormControl(props: {
setValue={setTeamAvatar}
className="w-20 rounded-full lg:w-28"
disableHelperText
fileUrl={teamUrl}
/>
</div>
</SettingsCard>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use server";

import type { Team } from "@/api/team";
import { API_SERVER_URL } from "@/constants/env";
import { getAuthToken } from "../../../../../../api/lib/getAuthToken";

export async function updateTeam(params: {
teamId: string;
value: Partial<Team>;
}) {
const authToken = getAuthToken();

if (!authToken) {
throw new Error("No auth token");
}

const res = await fetch(`${API_SERVER_URL}/v1/teams/${params.teamId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
body: JSON.stringify(params.value),
});

if (!res.ok) {
throw new Error("failed to update team");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ const freeTeam: Team = {
slug: "team-slug-foo-bar",
createdAt: "2023-07-07T19:21:33.604Z",
updatedAt: "2024-07-11T00:01:02.241Z",
deletedAt: null,
bannedAt: null,
billingStatus: "validPayment",
billingPlan: "free",
billingEmail: "[email protected]",
};

const proTeam: Team = {
Expand All @@ -52,10 +51,9 @@ const proTeam: Team = {
slug: "team-slug-foo-bar",
createdAt: "2023-07-07T19:21:33.604Z",
updatedAt: "2024-07-11T00:01:02.241Z",
deletedAt: null,
bannedAt: null,
billingStatus: "validPayment",
billingPlan: "pro",
billingEmail: "[email protected]",
};

function createMemberStub(id: string, role: TeamAccountRole): TeamMember {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { getTeamBySlug } from "@/api/team";
import { getThirdwebClient } from "@/constants/thirdweb.server";
import { notFound } from "next/navigation";
import { getAuthToken } from "../../../../../api/lib/getAuthToken";
import { TeamGeneralSettingsPage } from "./general/TeamGeneralSettingsPage";
Expand All @@ -15,7 +14,5 @@ export default async function Page(props: {
notFound();
}

return (
<TeamGeneralSettingsPage team={team} client={getThirdwebClient(token)} />
);
return <TeamGeneralSettingsPage team={team} authToken={token} />;
}
4 changes: 3 additions & 1 deletion apps/dashboard/src/components/shared/FileInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface IFileInputProps {
children?: React.ReactNode;
className?: string;
disableHelperText?: boolean;
fileUrl?: string;
}

export const FileInput: React.FC<IFileInputProps> = ({
Expand All @@ -45,6 +46,7 @@ export const FileInput: React.FC<IFileInputProps> = ({
className,
previewMaxWidth,
disableHelperText,
fileUrl: fileUrlFallback,
}) => {
const onDrop = useCallback<
<T extends File>(
Expand All @@ -67,7 +69,7 @@ export const FileInput: React.FC<IFileInputProps> = ({

const file: File | null =
typeof window !== "undefined" && value instanceof File ? value : null;
const fileUrl = useImageFileOrUrl(value);
const fileUrl = useImageFileOrUrl(value) || fileUrlFallback || "";

const helperTextOrFile = helperText
? helperText
Expand Down
3 changes: 1 addition & 2 deletions apps/dashboard/src/stories/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ export function teamStub(
billingStatus: "validPayment",
name: `Team ${id}`,
slug: `team-${id}`,
bannedAt: null,
createdAt: new Date().toISOString(),
deletedAt: null,
updatedAt: new Date().toISOString(),
billingEmail: "[email protected]",
};

return team;
Expand Down

0 comments on commit a90df36

Please sign in to comment.