Project Detail Page and Kanban Board

We have a placeholder page for the project detail at /projects/$projectId. Let’s update this page to show project information and the kanban board for that project. This will require some updates to our existing components to work with project-scoped data.

Update the Project Detail Page

Update src/routes/projects/$projectId.tsx:

import { createFileRoute } from "@tanstack/react-router";
import { useQuery } from "convex/react";
import { api } from "../../../convex/_generated/api";
import type { Id } from "../../../convex/_generated/dataModel";
import KanbanBoard from "@/components/kanban-board";

export const Route = createFileRoute("/projects/$projectId")({
  component: ProjectDetailPage,
});

function ProjectDetailPage() {
  const { projectId } = Route.useParams();
  const project = useQuery(api.projects.get, {
    id: projectId as Id<"projects">,
  });

  if (project === undefined) {
    return <p className="text-muted-foreground">Loading project...</p>;
  }

  if (project === null) {
    return <p className="text-destructive">Project not found.</p>;
  }

  return (
    <div>
      <div className="mb-6">
        <h2 className="text-xl font-semibold">{project.name}</h2>
        {project.description && (
          <p className="mt-1 text-sm text-muted-foreground">
            {project.description}
          </p>
        )}
      </div>
      <KanbanBoard projectId={project._id} />
    </div>
  );
}

The $projectId in the file path creates a dynamic route parameter. We extract it with Route.useParams() and pass it to useQuery(api.projects.get, ...) to fetch the project. Notice we handle two states: undefined (loading) and null (project does not exist).

The KanbanBoard component now receives a projectId prop. We need to update it.

Update KanbanBoard

Update src/components/kanban-board.tsx to accept a projectId prop and use it for queries:

  1. Define the prop type right above the KanbanBoard component definition:

    type KanbanBoardProps = {
      projectId: Id<"projects">;
    };
    
  2. Update the component signature to accept the prop:

    function KanbanBoard({ projectId }: KanbanBoardProps) { ... }
    
  3. Pass the projectId as prop to AddTaskDialog so new tasks are created in the right project:

    <AddTaskDialog projectId={projectId} />
    

We are not done with the KanbanBoard component yet. We will return to it in the next section. But first, we need to update the AddTaskDialog component to accept the projectId prop and use it when creating new tasks.

Update AddTaskDialog

Update src/components/add-task-dialog.tsx to accept a projectId prop:

  1. Import Id from the generated data model at the top of the file:

    import type { Id } from "../../convex/_generated/dataModel";
    
  2. Define the prop type right above the AddTaskDialog component definition:

    type AddTaskDialogProps = {
      projectId: Id<"projects">;
    };
    
  3. Update the component signature to accept the prop:

    function AddTaskDialog({ projectId }: AddTaskDialogProps) { ... }
    
  4. Update the handleSubmit function to include projectId when calling createTask:

    function handleSubmit(e: React.FormEvent) {
      e.preventDefault();
      if (!title.trim()) return;
      createTask({
        projectId,
        title: title.trim(),
        description: description.trim(),
      });
      setTitle("");
      setDescription("");
      setOpen(false);
    }
    

With these changes, when we create a new task from the AddTaskDialog, it will be associated with the correct project.

Checkpoint: Commit your progress.

git add .
git commit -m "planner-06: Update project detail page and kanban board to work with project-scoped data"
git push