🙆‍♀️

【Convex】NextJs14 と Convex【#30 Convex Delete Card】

2024/06/01に公開

【#30 Convex Delete Card】

YouTube: https://youtu.be/sXHdfW5lS-w
https://youtu.be/sXHdfW5lS-w

今回はカードの削除の処理を実装します。

そして、いきなり削除するのではなく
確認用のモーダルを表示して削除の処理を行います。

モーダルは「alert-dialog」を使用します。

npx shadcn-ui@latest add alert-dialog
app/(main)/_components/card/card-actions.tsx
"use client";

import { Pencil, Trash2 } from "lucide-react";
import { toast } from "sonner";

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useRenameCardModal } from "@/store/use-rename-card-modal";
import { useApiMutation } from "@/hooks/use-api-mutation";
import { api } from "@/convex/_generated/api";
import { Button } from "@/components/ui/button";
import { ConfirmModal } from "@/components/confirm-modal";

interface Props {
  children: React.ReactNode;
  id: string;
  title: string;
}

export const CardActions = ({ children, id, title }: Props) => {
  const { onOpen } = useRenameCardModal();
  const { pending, mutate } = useApiMutation(api.card.deleteCard);

  const onDelete = () => {
    mutate({ id })
      .then(() => toast.success("Card deleted"))
      .catch(() => toast.error("Failed to delete card"));
  };

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem
          className="p-3 cursor-pointer"
          onClick={() => onOpen(id, title)}
        >
          <Pencil className="h-4 w-4 mr-2" /> Rename
        </DropdownMenuItem>
        <ConfirmModal
          header="Are you sure you want to delete card?"
          description="All contents on the card will be deleted and cannot undone."
          disabled={pending}
          onConfirm={onDelete}
        >
          <Button
            variant="ghost"
            className="p-3 cursor-pointer text-sm w-full justify-start font-normal"
          >
            <Trash2 className="h-4 w-4 mr-2" /> Delete
          </Button>
        </ConfirmModal>
      </DropdownMenuContent>
    </DropdownMenu>
  );
};
components/confirm-modal.tsx
"use client";

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog";

interface Props {
  children: React.ReactNode;
  onConfirm: () => void;
  disabled?: boolean;
  header: string;
  description?: string;
}

export const ConfirmModal = ({
  children,
  onConfirm,
  disabled,
  header,
  description,
}: Props) => {
  const handleConfirm = () => {
    onConfirm();
  };

  return (
    <AlertDialog>
      <AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>{header}</AlertDialogTitle>
          <AlertDialogDescription>{description}</AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction disabled={disabled} onClick={handleConfirm}>
            Confirm
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
};

Discussion