From 2657aa6cdd299b5523d7f8adda417ad0f9b4447b Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Fri, 10 Apr 2026 05:23:14 +0530 Subject: [PATCH 1/3] feat: migrate artist delete to dedicated api --- app/api/artist/remove/route.ts | 19 ---------------- components/ArtistSetting/DeleteModal.tsx | 28 +++++++++++++++++++++--- components/Artists/DropDown.tsx | 20 +++++++++++++++-- lib/artists/deleteArtist.ts | 27 +++++++++++++++++++++++ 4 files changed, 70 insertions(+), 24 deletions(-) delete mode 100644 app/api/artist/remove/route.ts create mode 100644 lib/artists/deleteArtist.ts diff --git a/app/api/artist/remove/route.ts b/app/api/artist/remove/route.ts deleted file mode 100644 index 9edeaa21c..000000000 --- a/app/api/artist/remove/route.ts +++ /dev/null @@ -1,19 +0,0 @@ -import supabase from "@/lib/supabase/serverClient"; -import { NextRequest } from "next/server"; - -export async function GET(req: NextRequest) { - const artistId = req.nextUrl.searchParams.get("artistId"); - - try { - await supabase.from("accounts").delete().eq("id", artistId); - return Response.json({ success: true }, { status: 200 }); - } catch (error) { - console.error(error); - const message = error instanceof Error ? error.message : "failed"; - return Response.json({ message }, { status: 400 }); - } -} - -export const dynamic = "force-dynamic"; -export const fetchCache = "force-no-store"; -export const revalidate = 0; diff --git a/components/ArtistSetting/DeleteModal.tsx b/components/ArtistSetting/DeleteModal.tsx index 9b20d6c03..77467d529 100644 --- a/components/ArtistSetting/DeleteModal.tsx +++ b/components/ArtistSetting/DeleteModal.tsx @@ -1,23 +1,45 @@ import { useArtistProvider } from "@/providers/ArtistProvider"; import { ArtistRecord } from "@/types/Artist"; +import { usePrivy } from "@privy-io/react-auth"; +import { toast } from "sonner"; +import { deleteArtist } from "@/lib/artists/deleteArtist"; interface DeleteModalProps { toggleModal: () => void; } const DeleteModal = ({ toggleModal }: DeleteModalProps) => { - const { editableArtist, artists, setArtists, toggleSettingModal } = + const { editableArtist, artists, setArtists, toggleSettingModal, getArtists } = useArtistProvider(); + const { getAccessToken } = usePrivy(); const handleDelete = async () => { + const artistId = editableArtist?.account_id; + if (!artistId) { + return; + } + + const previousArtists = artists; const temp = artists.filter( (artistEle: ArtistRecord) => - artistEle.account_id !== editableArtist?.account_id, + artistEle.account_id !== artistId, ); setArtists([...temp]); toggleModal(); toggleSettingModal(); - await fetch(`/api/artist/remove?artistId=${editableArtist?.account_id}`); + + try { + const accessToken = await getAccessToken(); + if (!accessToken) { + throw new Error("Please sign in to delete an artist"); + } + + await deleteArtist(accessToken, artistId); + await getArtists(); + } catch (error) { + setArtists(previousArtists); + toast.error(error instanceof Error ? error.message : "Failed to delete artist"); + } }; return ( diff --git a/components/Artists/DropDown.tsx b/components/Artists/DropDown.tsx index 547d74a07..2e926866a 100644 --- a/components/Artists/DropDown.tsx +++ b/components/Artists/DropDown.tsx @@ -3,19 +3,35 @@ import { ArtistRecord } from "@/types/Artist"; import { Trash2 } from "lucide-react"; import { containerPatterns, textPatterns } from "@/lib/styles/patterns"; import { cn } from "@/lib/utils"; +import { usePrivy } from "@privy-io/react-auth"; +import { toast } from "sonner"; +import { deleteArtist } from "@/lib/artists/deleteArtist"; const DropDown = ({ artist }: { artist: ArtistRecord }) => { const { setArtists, artists, setMenuVisibleArtistId, getArtists } = useArtistProvider(); + const { getAccessToken } = usePrivy(); const handleDelete = async () => { + const previousArtists = artists; const temp = artists.filter( (artistEle: ArtistRecord) => artistEle.account_id !== artist.account_id, ); setArtists([...temp]); setMenuVisibleArtistId(null); - await fetch(`/api/artist/remove?artistId=${artist.account_id}`); - getArtists(); + + try { + const accessToken = await getAccessToken(); + if (!accessToken) { + throw new Error("Please sign in to delete an artist"); + } + + await deleteArtist(accessToken, artist.account_id); + await getArtists(); + } catch (error) { + setArtists(previousArtists); + toast.error(error instanceof Error ? error.message : "Failed to delete artist"); + } }; return ( diff --git a/lib/artists/deleteArtist.ts b/lib/artists/deleteArtist.ts new file mode 100644 index 000000000..b1c920126 --- /dev/null +++ b/lib/artists/deleteArtist.ts @@ -0,0 +1,27 @@ +import { getClientApiBaseUrl } from "@/lib/api/getClientApiBaseUrl"; + +interface DeleteArtistResponse { + success?: boolean; + error?: string; +} + +/** + * Deletes an artist through the dedicated API. + * + * @param accessToken - Privy access token for Bearer auth + * @param artistId - Artist account ID to delete + */ +export async function deleteArtist(accessToken: string, artistId: string): Promise { + const response = await fetch(`${getClientApiBaseUrl()}/api/artists/${artistId}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + const data: DeleteArtistResponse = await response.json(); + + if (!response.ok || !data.success) { + throw new Error(data.error || "Failed to delete artist"); + } +} From b21109b18c9a6aa4703b5a8bc89f3ff8a22edca3 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Fri, 10 Apr 2026 05:28:18 +0530 Subject: [PATCH 2/3] fix: avoid rollback after successful artist delete --- components/ArtistSetting/DeleteModal.tsx | 8 +++++++- components/Artists/DropDown.tsx | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/components/ArtistSetting/DeleteModal.tsx b/components/ArtistSetting/DeleteModal.tsx index 77467d529..a003e694f 100644 --- a/components/ArtistSetting/DeleteModal.tsx +++ b/components/ArtistSetting/DeleteModal.tsx @@ -35,10 +35,16 @@ const DeleteModal = ({ toggleModal }: DeleteModalProps) => { } await deleteArtist(accessToken, artistId); - await getArtists(); } catch (error) { setArtists(previousArtists); toast.error(error instanceof Error ? error.message : "Failed to delete artist"); + return; + } + + try { + await getArtists(); + } catch { + toast.error("Artist deleted, but failed to refresh the artist list"); } }; diff --git a/components/Artists/DropDown.tsx b/components/Artists/DropDown.tsx index 2e926866a..b1e13bf36 100644 --- a/components/Artists/DropDown.tsx +++ b/components/Artists/DropDown.tsx @@ -27,10 +27,16 @@ const DropDown = ({ artist }: { artist: ArtistRecord }) => { } await deleteArtist(accessToken, artist.account_id); - await getArtists(); } catch (error) { setArtists(previousArtists); toast.error(error instanceof Error ? error.message : "Failed to delete artist"); + return; + } + + try { + await getArtists(); + } catch { + toast.error("Artist deleted, but failed to refresh the artist list"); } }; From 4f5e58ae0d4adab22c3f656c029163804738f67b Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Fri, 10 Apr 2026 06:01:56 +0530 Subject: [PATCH 3/3] refactor: share artist delete flow in hook --- components/ArtistSetting/DeleteModal.tsx | 37 ++---------------- components/Artists/DropDown.tsx | 37 +++--------------- hooks/useDeleteArtist.ts | 50 ++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 64 deletions(-) create mode 100644 hooks/useDeleteArtist.ts diff --git a/components/ArtistSetting/DeleteModal.tsx b/components/ArtistSetting/DeleteModal.tsx index a003e694f..838c22e65 100644 --- a/components/ArtistSetting/DeleteModal.tsx +++ b/components/ArtistSetting/DeleteModal.tsx @@ -1,51 +1,22 @@ import { useArtistProvider } from "@/providers/ArtistProvider"; -import { ArtistRecord } from "@/types/Artist"; -import { usePrivy } from "@privy-io/react-auth"; -import { toast } from "sonner"; -import { deleteArtist } from "@/lib/artists/deleteArtist"; +import useDeleteArtist from "@/hooks/useDeleteArtist"; interface DeleteModalProps { toggleModal: () => void; } const DeleteModal = ({ toggleModal }: DeleteModalProps) => { - const { editableArtist, artists, setArtists, toggleSettingModal, getArtists } = - useArtistProvider(); - const { getAccessToken } = usePrivy(); + const { editableArtist, toggleSettingModal } = useArtistProvider(); + const { deleteArtist } = useDeleteArtist(); const handleDelete = async () => { const artistId = editableArtist?.account_id; if (!artistId) { return; } - - const previousArtists = artists; - const temp = artists.filter( - (artistEle: ArtistRecord) => - artistEle.account_id !== artistId, - ); - setArtists([...temp]); toggleModal(); toggleSettingModal(); - - try { - const accessToken = await getAccessToken(); - if (!accessToken) { - throw new Error("Please sign in to delete an artist"); - } - - await deleteArtist(accessToken, artistId); - } catch (error) { - setArtists(previousArtists); - toast.error(error instanceof Error ? error.message : "Failed to delete artist"); - return; - } - - try { - await getArtists(); - } catch { - toast.error("Artist deleted, but failed to refresh the artist list"); - } + await deleteArtist(artistId); }; return ( diff --git a/components/Artists/DropDown.tsx b/components/Artists/DropDown.tsx index b1e13bf36..57ba1df76 100644 --- a/components/Artists/DropDown.tsx +++ b/components/Artists/DropDown.tsx @@ -3,41 +3,16 @@ import { ArtistRecord } from "@/types/Artist"; import { Trash2 } from "lucide-react"; import { containerPatterns, textPatterns } from "@/lib/styles/patterns"; import { cn } from "@/lib/utils"; -import { usePrivy } from "@privy-io/react-auth"; -import { toast } from "sonner"; -import { deleteArtist } from "@/lib/artists/deleteArtist"; +import useDeleteArtist from "@/hooks/useDeleteArtist"; const DropDown = ({ artist }: { artist: ArtistRecord }) => { - const { setArtists, artists, setMenuVisibleArtistId, getArtists } = - useArtistProvider(); - const { getAccessToken } = usePrivy(); + const { setMenuVisibleArtistId } = useArtistProvider(); + const { deleteArtist } = useDeleteArtist(); const handleDelete = async () => { - const previousArtists = artists; - const temp = artists.filter( - (artistEle: ArtistRecord) => artistEle.account_id !== artist.account_id, - ); - setArtists([...temp]); - setMenuVisibleArtistId(null); - - try { - const accessToken = await getAccessToken(); - if (!accessToken) { - throw new Error("Please sign in to delete an artist"); - } - - await deleteArtist(accessToken, artist.account_id); - } catch (error) { - setArtists(previousArtists); - toast.error(error instanceof Error ? error.message : "Failed to delete artist"); - return; - } - - try { - await getArtists(); - } catch { - toast.error("Artist deleted, but failed to refresh the artist list"); - } + await deleteArtist(artist.account_id, { + onSuccess: () => setMenuVisibleArtistId(null), + }); }; return ( diff --git a/hooks/useDeleteArtist.ts b/hooks/useDeleteArtist.ts new file mode 100644 index 000000000..0aef78113 --- /dev/null +++ b/hooks/useDeleteArtist.ts @@ -0,0 +1,50 @@ +import { usePrivy } from "@privy-io/react-auth"; +import { toast } from "sonner"; +import { deleteArtist } from "@/lib/artists/deleteArtist"; +import { useArtistProvider } from "@/providers/ArtistProvider"; + +interface DeleteArtistOptions { + onSuccess?: () => void; +} + +interface UseDeleteArtistReturn { + deleteArtist: (artistId: string, options?: DeleteArtistOptions) => Promise; +} + +export default function useDeleteArtist(): UseDeleteArtistReturn { + const { getAccessToken } = usePrivy(); + const { artists, setArtists, getArtists } = useArtistProvider(); + + const deleteArtistHandler = async ( + artistId: string, + options?: DeleteArtistOptions, + ): Promise => { + const previousArtists = artists; + const nextArtists = artists.filter(artist => artist.account_id !== artistId); + setArtists(nextArtists); + + try { + const accessToken = await getAccessToken(); + if (!accessToken) { + throw new Error("Please sign in to delete an artist"); + } + + await deleteArtist(accessToken, artistId); + options?.onSuccess?.(); + } catch (error) { + setArtists(previousArtists); + toast.error(error instanceof Error ? error.message : "Failed to delete artist"); + return; + } + + try { + await getArtists(); + } catch { + toast.error("Artist deleted, but failed to refresh the artist list"); + } + }; + + return { + deleteArtist: deleteArtistHandler, + }; +}