diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx index 8649f8ab6a2..c28830221f5 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx @@ -52,6 +52,10 @@ export function ProjectGeneralSettingsPage(props: { paths: EditProjectUIPaths; onKeyUpdated: (() => void) | undefined; wording: "project" | "api-key"; + // TODO: remove this when this component is not used in the project settings page + hideFields?: { + name: boolean; + }; }) { const updateMutation = useUpdateApiKey(); const deleteMutation = useRevokeApiKey(); @@ -64,6 +68,7 @@ export function ProjectGeneralSettingsPage(props: { deleteMutation={deleteMutation} paths={props.paths} onKeyUpdated={props.onKeyUpdated} + hideFields={props.hideFields} /> ); } @@ -84,6 +89,9 @@ interface EditApiKeyProps { deleteMutation: DeleteMutation; paths: EditProjectUIPaths; onKeyUpdated: (() => void) | undefined; + hideFields?: { + name: boolean; + }; } type UpdateAPIForm = UseFormReturn; @@ -214,12 +222,14 @@ export const ProjectGeneralSettingsPageUI: React.FC = ( autoComplete="off" >
- + {!props.hideFields?.name && ( + + )} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPageForTeams.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPageForTeams.tsx index ac87800da7c..db2ea900f3b 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPageForTeams.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPageForTeams.tsx @@ -1,33 +1,106 @@ "use client"; +import type { Project } from "@/api/projects"; +import { SettingsCard } from "@/components/blocks/SettingsCard"; +import { Input } from "@/components/ui/input"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import type { ApiKey } from "@3rdweb-sdk/react/hooks/useApi"; +import { useMutation } from "@tanstack/react-query"; +import { useState } from "react"; +import { toast } from "sonner"; import { ProjectGeneralSettingsPage } from "./ProjectGeneralSettingsPage"; +import { updateProject } from "./updateProject"; export function ProjectGeneralSettingsPageForTeams(props: { team_slug: string; - project_slug: string; + project: Project; apiKey: ApiKey; + teamId: string; }) { const router = useDashboardRouter(); - const { team_slug, project_slug, apiKey } = props; - const projectSettingsLayout = `/team/${team_slug}/${project_slug}/settings`; + const { team_slug, project, apiKey } = props; + const projectSettingsLayout = `/team/${team_slug}/${project.slug}/settings`; - // TODO - add a Project Image form field on this page + return ( +
+ { + await updateProject({ + projectId: project.id, + teamId: props.teamId, // TODO: remove this when project.teamId is fixed in api server + value: { + name, + }, + }); + router.refresh(); + }} + /> + + {/* TODO - replace this when we have project services endpoints */} + { + router.refresh(); + }} + wording="project" + hideFields={{ + name: true, + }} + /> +
+ ); +} + +function ProjectNameSetting(props: { + name: string; + update: (name: string) => Promise; +}) { + const [name, setName] = useState(props.name); + const updateName = useMutation({ + mutationFn: props.update, + }); + + const errorText = name === "" ? "Project name is required" : undefined; return ( - { - router.refresh(); + noPermissionText={undefined} + errorText={errorText} + saveButton={{ + onClick: () => { + const promise = updateName.mutateAsync(name); + toast.promise(promise, { + success: "Project name updated successfully", + error: "Failed to update project name", + }); + }, + disabled: false, + isPending: updateName.isPending, }} - wording="project" - /> + bottomText="Please use 64 characters at maximum" + > + { + setName(e.target.value); + }} + className="max-w-[350px] bg-background" + /> + ); } diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/page.tsx index 5f57a4509d5..0bf9619157f 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/page.tsx @@ -1,4 +1,5 @@ import { getProject } from "@/api/projects"; +import { getTeamBySlug } from "@/api/team"; import { notFound } from "next/navigation"; import { getAPIKeyForProjectId } from "../../../../api/lib/getAPIKeys"; import { ProjectGeneralSettingsPageForTeams } from "./ProjectGeneralSettingsPageForTeams"; @@ -7,6 +8,14 @@ export default async function Page(props: { params: { team_slug: string; project_slug: string }; }) { const { team_slug, project_slug } = props.params; + + // TODO: remove this when project.teamId is fixed in api server + const team = await getTeamBySlug(team_slug); + + if (!team) { + notFound(); + } + const project = await getProject(team_slug, project_slug); if (!project) { @@ -22,8 +31,9 @@ export default async function Page(props: { return ( ); } diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/updateProject.ts b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/updateProject.ts new file mode 100644 index 00000000000..ae6db2c250a --- /dev/null +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/updateProject.ts @@ -0,0 +1,33 @@ +"use server"; + +import type { Project } from "@/api/projects"; +import { API_SERVER_URL } from "@/constants/env"; +import { getAuthToken } from "../../../../api/lib/getAuthToken"; + +export async function updateProject(params: { + projectId: string; + teamId: string; + value: Partial; +}) { + const authToken = getAuthToken(); + + if (!authToken) { + throw new Error("No auth token"); + } + + const res = await fetch( + `${API_SERVER_URL}/v1/teams/${params.teamId}/projects/${params.projectId}`, + { + 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"); + } +}