diff --git a/src/app/(home)/_components/list-button.tsx b/src/app/(home)/_components/list-button.tsx index 68f6d34..16d6d59 100644 --- a/src/app/(home)/_components/list-button.tsx +++ b/src/app/(home)/_components/list-button.tsx @@ -3,6 +3,8 @@ import {} from "@radix-ui/react-dropdown-menu"; import { Dot, MoreHorizontal } from "lucide-react"; import Link from "next/link"; +import { toast } from "sonner"; +import { useDialogConfirmation } from "~/app/_components/macro/confirmation-dialog"; import { Button } from "~/app/_components/ui/button"; import { DropdownMenu, @@ -12,29 +14,49 @@ import { } from "~/app/_components/ui/dropdown-menu"; import { api } from "~/trpc/react"; -export const ListButton = ({ id, name }: { id: number; name: string }) => { - const newName = "test new name"; +const newName = "test new name"; - const { mutate: updateMutation } = api.list.update.useMutation(); - const { mutate: deleteMutation } = api.list.delete.useMutation(); +export const ListButton = ({ id, name }: { id: number; name: string }) => { + const dialogConfirmation = useDialogConfirmation(); + + const { mutate: updateMutation } = api.list.update.useMutation({ + onSuccess: () => { + toast.success("List successfully renamed."); + }, + }); + const { mutate: deleteMutation } = api.list.delete.useMutation({ + onSuccess: () => { + toast.success("List successfully deleted."); + }, + }); const handleRename = () => updateMutation({ listId: id, name: newName }); - const handleDelete = () => deleteMutation({ listId: id }); + + const handleDelete = async () => { + const didConfirm = await dialogConfirmation({}); + if (didConfirm) { + deleteMutation({ listId: id }); + } + }; return ( - + <> - {name} + + {name} + Open menu @@ -45,7 +67,7 @@ export const ListButton = ({ id, name }: { id: number; name: string }) => { Delete - + > ); }; diff --git a/src/app/_components/macro/confirmation-dialog.tsx b/src/app/_components/macro/confirmation-dialog.tsx new file mode 100644 index 0000000..395e0b1 --- /dev/null +++ b/src/app/_components/macro/confirmation-dialog.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "~/app/_components/ui/dialog"; +import { Button } from "~/app/_components/ui/button"; +import { createContext, useContext, useState } from "react"; + +type DialogContextType = (options: { + title?: string; + description?: string; + confirmText?: string; + cancelText?: string; +}) => Promise; + +const DialogContext = createContext(null); + +export const useDialogConfirmation = () => { + const context = useContext(DialogContext); + if (!context) { + throw new Error("useDialogConfirmation must be used within DialogProvider"); + } + return context; +}; + +export const DialogProvider = ({ children }: { children: React.ReactNode }) => { + const [dialogState, setDialogState] = useState<{ + isOpen: boolean; + options: { + title?: string; + description?: string; + confirmText?: string; + cancelText?: string; + }; + resolve: ((value: boolean) => void) | null; + }>({ + isOpen: false, + options: {}, + resolve: null, + }); + + const dialogConfirmation: DialogContextType = (options) => + new Promise((resolve) => { + setDialogState({ + isOpen: true, + options, + resolve, + }); + }); + + const handleConfirm = () => { + if (dialogState.resolve) dialogState.resolve(true); + setDialogState({ ...dialogState, isOpen: false }); + }; + + const handleCancel = () => { + if (dialogState.resolve) dialogState.resolve(false); + setDialogState({ ...dialogState, isOpen: false }); + }; + + return ( + + {children} + setDialogState({ ...dialogState, isOpen })} + > + + + + {dialogState.options.title ?? "Are you sure?"} + + + + {dialogState.options.description ?? "This action cannot be undone."} + + + + {dialogState.options.cancelText ?? "Cancel"} + + + {dialogState.options.confirmText ?? "Confirm"} + + + + + + ); +}; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a4425f8..4f9f374 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,7 +5,9 @@ import { type Metadata } from "next"; import { TRPCReactProvider } from "~/trpc/react"; import { HydrateClient } from "~/trpc/server"; -import { AuthProvider } from "~/app/_components/auth-provider"; +import { AuthProvider } from "~/app/_components/providers/auth-provider"; +import { DialogProvider } from "~/app/_components/macro/confirmation-dialog"; +import { Toaster } from "~/app/_components/ui/sonner"; export const metadata: Metadata = { title: "ls", @@ -26,7 +28,12 @@ export default function RootLayout({ - {children} + + + {children} + + +
+ {dialogState.options.description ?? "This action cannot be undone."} +