diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 83d53be..49fa2e1 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -325,6 +325,10 @@ export type Database = { [_ in never]: never } Functions: { + delete_user: { + Args: Record + Returns: undefined + } has_role: { Args: { _role: Database["public"]["Enums"]["app_role"] diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index f047f8e..ee317d3 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -5,8 +5,19 @@ import { Input } from "@/components/ui/input"; import { toast } from "sonner"; import { supabase } from "@/integrations/supabase/client"; import { Toaster } from "sonner"; -import { ArrowLeft, Loader2 } from "lucide-react"; -import { Link } from "react-router-dom"; +import { ArrowLeft, Loader2, Trash2 } from "lucide-react"; +import { Link, useNavigate } from "react-router-dom"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; import { Form, FormControl, @@ -31,8 +42,10 @@ const passwordSchema = z.object({ const Settings = () => { - const { user } = useAuth(); + const { user, signOut } = useAuth(); + const navigate = useNavigate(); const [isChangingPassword, setIsChangingPassword] = useState(false); + const [isDeletingAccount, setIsDeletingAccount] = useState(false); const form = useForm>({ resolver: zodResolver(passwordSchema), @@ -77,6 +90,28 @@ const Settings = () => { } }; + const handleDeleteAccount = async () => { + try { + setIsDeletingAccount(true); + + // Call the delete_user RPC function which will delete the user and all related data + const { error } = await supabase.rpc('delete_user'); + + if (error) { + toast.error("Failed to delete account: " + error.message); + return; + } + + toast.success("Account deleted successfully"); + await signOut(); + navigate("/auth"); + } catch (error: any) { + toast.error("Error deleting account: " + error.message); + } finally { + setIsDeletingAccount(false); + } + }; + return (
@@ -177,6 +212,57 @@ const Settings = () => {
+ +
+

Danger Zone

+
+
+

+ Once you delete your account, there is no going back. All your data including portfolio, projects, and analytics will be permanently deleted. +

+ + + + + + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete your account + and remove all your data from our servers including your portfolio, projects, + sections, and analytics data. + + + + Cancel + + Yes, delete my account + + + + +
+
+
diff --git a/supabase/migrations/20251016173755_0bcf3c69-ffaf-4fec-a6e6-bd361addad3b.sql b/supabase/migrations/20251016173755_0bcf3c69-ffaf-4fec-a6e6-bd361addad3b.sql new file mode 100644 index 0000000..9a3a8e7 --- /dev/null +++ b/supabase/migrations/20251016173755_0bcf3c69-ffaf-4fec-a6e6-bd361addad3b.sql @@ -0,0 +1,24 @@ +-- Function to delete the current user's account +-- This will cascade delete all related data due to foreign key constraints +CREATE OR REPLACE FUNCTION public.delete_user() +RETURNS void +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + current_user_id uuid; +BEGIN + -- Get the current user's ID + current_user_id := auth.uid(); + + -- Ensure user is authenticated + IF current_user_id IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + -- Delete the user from auth.users + -- This will cascade delete all related data in public schema tables + DELETE FROM auth.users WHERE id = current_user_id; +END; +$$; \ No newline at end of file