diff --git a/components/organisms/UserSettingsPage/user-settings-page.tsx b/components/organisms/UserSettingsPage/user-settings-page.tsx index 69f99e04d1..928e8225b6 100644 --- a/components/organisms/UserSettingsPage/user-settings-page.tsx +++ b/components/organisms/UserSettingsPage/user-settings-page.tsx @@ -22,6 +22,7 @@ import { useFetchUser } from "lib/hooks/useFetchUser"; import { getInterestOptions } from "lib/utils/getInterestOptions"; import { useToast } from "lib/hooks/useToast"; import { validateTwitterUsername } from "lib/utils/validate-twitter-username"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "components/molecules/Dialog/dialog"; import CouponForm from "./coupon-form"; interface userSettingsPageProps { @@ -32,7 +33,65 @@ type EmailPreferenceType = { display_email?: boolean; receive_collaboration?: boolean; }; + +interface DeleteAccountModalProps { + open: boolean; + setOpen: (open: boolean) => void; + onDelete: () => void; +} + +const DeleteAccountModal = ({ open, setOpen, onDelete }: DeleteAccountModalProps) => { + const [confirmText, setConfirmText] = useState(""); + const disabled = confirmText !== "DELETE"; + + return ( + + + + Delete Account + +
+ Are you sure you want to delete your account? + + Type DELETE in all caps to confirm + + { + setConfirmText(e.target.value); + }} + /> +
+ + +
+
+
+
+ ); +}; + const UserSettingsPage = ({ user }: userSettingsPageProps) => { + const [isModalOpen, setIsModalOpen] = useState(false); + const deleteFormRef = useRef(null); const { data: insightsUser, mutate } = useFetchUser(user?.user_metadata.user_name, { revalidateOnFocus: false, }); @@ -389,44 +448,84 @@ const UserSettingsPage = ({ user }: userSettingsPageProps) => { {userInfo && ( -
- {!hasReports && !coupon ? ( -
-
- -
- Upgrade to a subscription to gain access to generate custom reports! -
-
- - - {!coupon && } -
- ) : ( -
+ <> +
+ {!hasReports && !coupon ? (
- -
- - You are currently subscribed to the Pro plan and currently have access to all premium - features. - + +
+ Upgrade to a subscription to gain access to generate custom reports!
- + + + {!coupon && } +
+ ) : ( +
+
+
+ +
+ + You are currently subscribed to the Pro plan and currently have access to all premium + features. + +
+
+ +
+
+ )} +
+
{ + setIsModalOpen(true); + e.preventDefault(); + }} + > +
+ +
+ + Please note that account deletion is irreversible. Proceed only if you are certain about this + action. +
- )} -
+ + { + setIsModalOpen(false); + deleteFormRef.current?.submit(); + }} + /> + + )}
diff --git a/middleware.ts b/middleware.ts index b5d43c52bc..864fb5d67b 100644 --- a/middleware.ts +++ b/middleware.ts @@ -12,6 +12,7 @@ const pathsToMatch = [ "/feed/", "/user/notifications", "/user/settings", + "/account-deleted" ]; export async function middleware(req: NextRequest) { @@ -28,6 +29,14 @@ export async function middleware(req: NextRequest) { data: { session }, } = await supabase.auth.getSession(); + if (session?.user && req.nextUrl.pathname === "/account-deleted") { + // Delete the account from Supabase and log the user out. + await supabase.auth.admin.deleteUser(session.user.id); + await supabase.auth.signOut(); + + return res; + } + // Check auth condition if (session?.user || req.nextUrl.searchParams.has("login")) { // Authentication successful, forward request to protected route. diff --git a/pages/account-deleted.tsx b/pages/account-deleted.tsx new file mode 100644 index 0000000000..466139bb8c --- /dev/null +++ b/pages/account-deleted.tsx @@ -0,0 +1,23 @@ +import Head from "next/head"; +import TopNav from "components/organisms/TopNav/top-nav"; + +const AccountDeletedPage = () => { + return ( + <> + + + +
+ +
+
+

Account Deleted

+

Your account has been deleted.

+
+
+
+ + ); +}; + +export default AccountDeletedPage; diff --git a/pages/api/delete-account.ts b/pages/api/delete-account.ts new file mode 100644 index 0000000000..dc674cd2a6 --- /dev/null +++ b/pages/api/delete-account.ts @@ -0,0 +1,45 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { createPagesServerClient } from "@supabase/auth-helpers-nextjs"; +import { fetchApiData } from "helpers/fetchApiData"; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== "POST") { + res.status(405).json({ + message: "Method not allowed", + }); + } + + const supabaseServerClient = createPagesServerClient({ + req, + res, + }); + + try { + const { + data: { session }, + } = await supabaseServerClient.auth.getSession(); + + if (session) { + const { error } = await fetchApiData({ + path: "/profile", + method: "DELETE", + bearerToken: session.access_token, + pathValidator: () => true, + }); + + if (error) { + throw error; + } + } else { + res.status(401).json({ + message: "Unauthorized", + }); + } + + res.redirect("/account-deleted"); + } catch (error) { + res.status(500).json({ + message: "Internal server error", + }); + } +}