From 3dbafe965322f0b150c8e65096a435fee1990492 Mon Sep 17 00:00:00 2001 From: Manan Tank Date: Tue, 25 Feb 2025 23:57:44 +0530 Subject: [PATCH] [TOOL-3525] Dashboard: Add Delete Account in account settings page --- apps/dashboard/src/@/api/team.ts | 17 +++ .../@/components/blocks/DangerSettingCard.tsx | 40 +++-- .../account/settings/AccountSettingsPage.tsx | 29 +++- .../AccountSettingsPageUI.stories.tsx | 47 ++++++ .../settings/AccountSettingsPageUI.tsx | 142 +++++++++++++++--- .../src/app/account/settings/page.tsx | 18 ++- apps/dashboard/src/app/team/page.tsx | 7 +- apps/dashboard/src/middleware.ts | 6 +- 8 files changed, 257 insertions(+), 49 deletions(-) diff --git a/apps/dashboard/src/@/api/team.ts b/apps/dashboard/src/@/api/team.ts index ef51d5ca776..d4516c6c78c 100644 --- a/apps/dashboard/src/@/api/team.ts +++ b/apps/dashboard/src/@/api/team.ts @@ -57,6 +57,23 @@ export async function getTeams() { return null; } +export async function getDefaultTeam() { + const token = await getAuthToken(); + if (!token) { + return null; + } + + const res = await fetch(`${API_SERVER_URL}/v1/teams/~`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + if (res.ok) { + return (await res.json())?.result as Team; + } + return null; +} + type TeamNebulaWaitList = { onWaitlist: boolean; createdAt: null | string; diff --git a/apps/dashboard/src/@/components/blocks/DangerSettingCard.tsx b/apps/dashboard/src/@/components/blocks/DangerSettingCard.tsx index 6b76e427854..003a4214eaa 100644 --- a/apps/dashboard/src/@/components/blocks/DangerSettingCard.tsx +++ b/apps/dashboard/src/@/components/blocks/DangerSettingCard.tsx @@ -10,7 +10,9 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import { useState } from "react"; import { cn } from "../../lib/utils"; +import { DynamicHeight } from "../ui/DynamicHeight"; export function DangerSettingCard(props: { title: string; @@ -24,9 +26,14 @@ export function DangerSettingCard(props: { confirmationDialog: { title: string; description: React.ReactNode; + children?: React.ReactNode; + onClose?: () => void; }; children?: React.ReactNode; }) { + const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = + useState(false); + return (
- + { + setIsConfirmationDialogOpen(v); + if (!v) { + props.confirmationDialog.onClose?.(); + } + }} + > + + +
+ )} + + {deleteAccount.data?.status === 402 && ( +
+ + Failed to delete account + + + Your default team "{props.defaultTeamName}" has unpaid + invoices. These invoices have to be paid before deleting + account + + + Manage Billing + + + +
+ )} + + ), }} /> ); diff --git a/apps/dashboard/src/app/account/settings/page.tsx b/apps/dashboard/src/app/account/settings/page.tsx index 4b9927f1bb0..26d76ce7173 100644 --- a/apps/dashboard/src/app/account/settings/page.tsx +++ b/apps/dashboard/src/app/account/settings/page.tsx @@ -1,3 +1,4 @@ +import { getDefaultTeam } from "@/api/team"; import { getThirdwebClient } from "@/constants/thirdweb.server"; import { getAuthToken } from "../../api/lib/getAuthToken"; import { loginRedirect } from "../../login/loginRedirect"; @@ -6,14 +7,23 @@ import { getValidAccount } from "./getAccount"; export default async function Page() { const pagePath = "/account"; - const account = await getValidAccount(pagePath); - const token = await getAuthToken(); - if (!token) { + const [defaultTeam, account, token] = await Promise.all([ + getDefaultTeam(), + getValidAccount(pagePath), + getAuthToken(), + ]); + + if (!token || !defaultTeam) { loginRedirect(pagePath); } return ( - + ); } diff --git a/apps/dashboard/src/app/team/page.tsx b/apps/dashboard/src/app/team/page.tsx index f7c21e7d292..604ef025e47 100644 --- a/apps/dashboard/src/app/team/page.tsx +++ b/apps/dashboard/src/app/team/page.tsx @@ -1,11 +1,8 @@ -import { getTeams } from "@/api/team"; +import { getDefaultTeam } from "@/api/team"; import { notFound, redirect } from "next/navigation"; export default async function TeamRootPage() { - // get all teams, then go to the first team - const teams = await getTeams(); - - const firstTeam = teams?.[0]; + const firstTeam = await getDefaultTeam(); if (!firstTeam) { notFound(); } diff --git a/apps/dashboard/src/middleware.ts b/apps/dashboard/src/middleware.ts index cb702403f7b..55f71eaab4c 100644 --- a/apps/dashboard/src/middleware.ts +++ b/apps/dashboard/src/middleware.ts @@ -1,4 +1,4 @@ -import { getTeams } from "@/api/team"; +import { getDefaultTeam } from "@/api/team"; import { isLoginRequired } from "@/constants/auth"; import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie"; import { type NextRequest, NextResponse } from "next/server"; @@ -165,9 +165,7 @@ export async function middleware(request: NextRequest) { // redirect /team/~/... to /team//... if (paths[0] === "team" && paths[1] === "~") { - // TODO - need an API to get the first team to avoid fetching all teams - const teams = await getTeams(); - const firstTeam = teams?.[0]; + const firstTeam = await getDefaultTeam(); if (firstTeam) { const modifiedPaths = [...paths]; modifiedPaths[1] = firstTeam.slug;