Kanban Board
The Kanban board is the primary visual interface for managing deals within a pipeline. It displays deal cards organized into stage columns, with drag-and-drop support for moving deals between stages.
Board Layout
The board is accessed at /deals/[pipelineId] and displays:
- Header — Pipeline name (with a breadcrumb link back to "All Pipelines"), a Manage button (Admin+), and a New Deal button.
- Stage Columns — One column per stage, ordered by
order_index. Each column header shows the stage name and the total value of deals in that column.
Responsive Behavior
| Viewport | Behavior |
|---|---|
| Desktop | Horizontal column layout with horizontal scrolling. Minimum width scales with the number of stages (stages × 300px). |
| Mobile | Vertical stack. Each stage is a full-width section. Drag-and-drop is replaced by a Move to Stage dialog (tap the ⋮ kebab menu). |
Deal Cards
Each deal is displayed as a card within its stage column showing:
- Title (clickable link to the deal detail page)
- Value (formatted as currency)
- Contact name
- Company name (if linked)
Kebab Menu (⋮)
Each card has a kebab menu with:
| Action | Description | Restriction |
|---|---|---|
| View Details | Navigate to the deal detail page | All roles |
| Edit | Open the edit deal dialog | All roles |
| Move to Stage | Open a dialog to select a different stage (mobile alternative to drag) | All roles |
| Mark as Won | Set status to won, remove from board | All roles |
| Mark as Lost | Set status to lost, remove from board | All roles |
| Delete | Delete the deal after confirmation | Admin+ |
Drag-and-Drop
The board uses the @dnd-kit library for desktop drag-and-drop:
How It Works
- Drag Start — Picks up a deal card, showing a drag overlay with the deal title and value.
- Drag Over — Highlights the target stage column using closest-corner collision detection.
- Drop — Moves the deal to the new stage.
Optimistic Updates
When a deal is dropped on a new stage:
- The UI immediately updates the card's position (optimistic update).
- The
moveDealToStageserver action is called in the background. - If the server action fails, the card reverts to its original position and an error toast is shown.
This pattern ensures the board feels responsive even on slower connections.
Activation Constraint
A minimum drag distance of 8 pixels is required before a drag starts. This prevents accidental drags when the user intends to click the card or open the kebab menu.
Hydration Safety
Since @dnd-kit uses browser-only APIs, the Kanban board uses a client-side mount guard to prevent
Next.js hydration mismatch errors:
1. Component mounts → isMounted = false → renders skeleton columns
2. useEffect fires → isMounted = true → renders DndContext with full boardThis ensures server-rendered HTML matches the initial client render before the drag-and-drop context is mounted.
Terminal States
When a deal is marked as Won or Lost (via the kebab menu or the detail page), it is
removed from the active board. The Kanban board only displays deals with status: "open".
Won and lost deals remain accessible from:
- The All Deals table on the pipeline landing page (filtered by status).
- The Deals tab on Contact and Company detail pages.
- The deal detail page directly (via URL or search).
This design keeps the active board focused on actionable deals while preserving historical data.