Wire the Edit Dialog to the Dropdown Menu

We will update src/components/issue-card.tsx to include the Edit and Delete menu items behind the state-conditional check, and render the edit dialog.

Replace the existing code in src/components/issue-card.tsx with the full code below:

import { useState } from "react";
import { useDraggable } from "@dnd-kit/core";
import { MoreHorizontal, Pencil, Trash2 } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useMutation, useQuery } from "convex/react";
import useUpdateIssueStatus from "@/hooks/use-update-issue-status";
import EditIssueDialog from "@/components/edit-issue-dialog";
import { api } from "../../convex/_generated/api";
import type { Doc } from "../../convex/_generated/dataModel";

type IssueCardProps = {
  issue: Doc<"issues">;
  isOverlay?: boolean;
};

function IssueCard({ issue, isOverlay }: IssueCardProps) {
  const updateStatus = useUpdateIssueStatus(issue.projectId);
  const removeIssue = useMutation(api.issues.remove);
  const currentUser = useQuery(api.users.currentUser);
  const [editOpen, setEditOpen] = useState(false);

  const isCreator = currentUser?._id === issue.creatorId;
  const canEditOrDelete = isCreator && issue.status === "todo";

  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
    id: issue._id,
    disabled: isOverlay,
  });

  return (
    <>
      <Card
        ref={isOverlay ? undefined : setNodeRef}
        className={`transition-shadow hover:shadow-md ${isDragging ? "opacity-0" : ""} ${isOverlay ? "shadow-lg ring-2 ring-primary opacity-100" : ""}`}
      >
        <CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2">
          <CardTitle
            className="flex-1 cursor-grab text-sm font-medium leading-snug active:cursor-grabbing"
            {...listeners}
            {...attributes}
          >
            {issue.title}
          </CardTitle>
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="ghost" size="icon" className="h-7 w-7 shrink-0">
                <MoreHorizontal className="h-4 w-4" />
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end" className="w-48">
              {issue.status !== "todo" && (
                <DropdownMenuItem
                  onClick={() =>
                    updateStatus({ id: issue._id, status: "todo" })
                  }
                >
                  Move to To Do
                </DropdownMenuItem>
              )}
              {issue.status !== "in-progress" && (
                <DropdownMenuItem
                  onClick={() =>
                    updateStatus({ id: issue._id, status: "in-progress" })
                  }
                >
                  Move to In Progress
                </DropdownMenuItem>
              )}
              {issue.status !== "done" && (
                <DropdownMenuItem
                  onClick={() =>
                    updateStatus({ id: issue._id, status: "done" })
                  }
                >
                  Move to Done
                </DropdownMenuItem>
              )}
              {canEditOrDelete && (
                <>
                  <DropdownMenuSeparator />
                  <DropdownMenuItem onClick={() => setEditOpen(true)}>
                    <Pencil className="mr-2 h-4 w-4" />
                    Edit
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    className="text-destructive"
                    onClick={() => removeIssue({ id: issue._id })}
                  >
                    <Trash2 className="mr-2 h-4 w-4" />
                    Delete
                  </DropdownMenuItem>
                </>
              )}
            </DropdownMenuContent>
          </DropdownMenu>
        </CardHeader>
        {issue.description && (
          <CardContent>
            <p className="line-clamp-2 text-sm text-muted-foreground">
              {issue.description}
            </p>
          </CardContent>
        )}
      </Card>
      {!isOverlay && (
        <EditIssueDialog
          key={issue._id}
          issue={issue}
          open={editOpen}
          onOpenChange={setEditOpen}
        />
      )}
    </>
  );
}

export default IssueCard;

The key line is:

const canEditOrDelete = isCreator && issue.status === "todo";

That one boolean is the whole rule on the frontend. It matches the server-side guard exactly.

The rest of the changes are structural:

  • The component wraps everything in a <>…</> fragment so it can render the <EditIssueDialog> as a sibling of the card. Dialogs need to be outside any draggable container for their portals to work correctly.
  • We render the dialog only when !isOverlay. This is a dnd-kit detail. When a card is being dragged, dnd-kit renders a second instance of the same component as a drag overlay, and only one dialog should exist at a time.
  • The useState for editOpen controls the dialog. Clicking the Edit menu item opens it. The dialog’s Cancel button or a successful save closes it.

Try It Out

The happy case

Sign in. Open a project that has issues in all three columns. Click the ⋯ menu on an issue in To Do. You will see the move items, then a separator, then Edit and Delete. Click Edit, change the title, save. The card updates instantly.

Dropdown Menu - Edit/Delete

The state-conditional case

Click the ⋯ menu on an issue in In Progress or Done. The menu only shows move items. There is no separator, and no Edit or Delete item. You are still the creator, but the status is not todo, so the rule denies edit and delete, and the menu shows that. If you move that issue back to To Do (via a move item in the menu), the Edit and Delete items come back right away.

Dropdown Menu - Move Only

Checkpoint: Commit your progress.

git add .
git commit -m "tracker-12: Wire the Edit Dialog to the Dropdown Menu"
git push