Using Convex Document Types

We need to update Column and TaskCard to use Convex’s document type. Convex generates a Doc helper type from your schema.

Update src/components/column.tsx:

import { Badge } from "@/components/ui/badge";
import TaskCard from "@/components/task-card";
import type { Doc } from "../../convex/_generated/dataModel";

type ColumnProps = {
  title: string;
  status: string;
  tasks: Doc<"tasks">[];
};

function Column({ title, status, tasks }: ColumnProps) {
  const borderColor: Record<string, string> = {
    todo: "border-l-blue-500",
    "in-progress": "border-l-amber-500",
    done: "border-l-green-500",
  };

  return (
    <div
      className={`rounded-lg border-l-4 bg-muted/50 p-4 ${borderColor[status]}`}
    >
      <div className="mb-4 flex items-center justify-between">
        <h2 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
          {title}
        </h2>
        <Badge variant="secondary">{tasks.length}</Badge>
      </div>
      <div className="space-y-3">
        {tasks.length === 0 ? (
          <p className="py-8 text-center text-sm text-muted-foreground">
            No tasks
          </p>
        ) : (
          tasks.map((task) => <TaskCard key={task._id} task={task} />)
        )}
      </div>
    </div>
  );
}

export default Column;

The Doc type takes a table name as a parameter and returns the TypeScript type for documents in that table. It includes all the fields you defined in the schema, plus the system fields (_id and _creationTime).

Notice task.id became task._id. That is the Convex document ID.

Update src/components/task-card.tsx:

import { MoreHorizontal, 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 type { Doc } from "../../convex/_generated/dataModel";

type TaskCardProps = {
  task: Doc<"tasks">;
};

function TaskCard({ task }: TaskCardProps) {
  return (
    <Card className="transition-shadow hover:shadow-md">
      <CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2">
        <CardTitle className="text-sm font-medium leading-snug">
          {task.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">
            {task.status !== "todo" && (
              <DropdownMenuItem>Move to To Do</DropdownMenuItem>
            )}
            {task.status !== "in-progress" && (
              <DropdownMenuItem>Move to In Progress</DropdownMenuItem>
            )}
            {task.status !== "done" && (
              <DropdownMenuItem>Move to Done</DropdownMenuItem>
            )}
            <DropdownMenuSeparator />
            <DropdownMenuItem className="text-destructive">
              <Trash2 className="mr-2 h-4 w-4" />
              Delete
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </CardHeader>
      {task.description && (
        <CardContent>
          <p className="line-clamp-2 text-sm text-muted-foreground">
            {task.description}
          </p>
        </CardContent>
      )}
    </Card>
  );
}

export default TaskCard;

Notice we removed the onClick handlers from the menu items. The old handlers called moveTask and deleteTask from the TanStack Store, but we are now reading from Convex, not the store. Those store functions would modify localStorage, which the app no longer reads. We will add Convex mutations in the next section to make these buttons work again.

Try It Out

Run the app. You should see “Loading tasks…” briefly, then an empty board. The Convex database has no tasks yet. The old sample tasks were in localStorage, which we are no longer reading.

The “Add Task” button still calls the store’s addTask function, so it will not add tasks to Convex either. We will fix all of this once we write mutations.

Empty Board

Checkpoint: Commit your progress.

git add .
git commit -m "kanban-06: Update components to use Convex document types"
git push