Skip to content

Kanban Board

Reusable drag-and-drop kanban block powered by react-kanban-kit with shadcn cards, sprint mock data, and board helpers.

Open in

Sprint 24 — Verifyman platform

Ship vendor-request queue routing, registry kanban block, and staging QA for consent + auth.

13 cards43 pts5 columns

Backlog

3
Vendor request queue handler

Consume VERIFICATION_REQUEST_CREATED, persist vendor_requests, emit PUSH_NOTIFICATION.

HighBackendVerifyman
SKSara K.8 pt
Push service fan-out cleanup

Remove VR-specific logic from push-services; generic PUSH_NOTIFICATION only.

MediumRefactor
AMAlex M.5 pt
Document primary-queue routing
LowDocs
2 pt

To Do

3
Kanban board registry block

react-kanban-kit + shadcn cards, Storybook full example, mock data.

Highwoxcn-registryUI
JPJordan P.5 pt
Social schedule calendar QA

Verify schedule sheet CRUD and platform colors on staging.

MediumQA
MLMina L.3 pt
CI affected-package filter
MediumDevOps
AMAlex M.3 pt

In Progress

2
Consent reminder cron

Daily 9:00 UTC job; max 3 reminders per consent.

HighCronEmail
SKSara K.5 pt
Authentication OTP email routing

Local → email-staging; staging/prod → EMAIL_SERVICE binding.

HighAuth
JPJordan P.3 pt

In Review

2
Marketing site SEO pass

Update index.html meta, OG tags, remove Lovable placeholders.

MediumMarketing
MLMina L.3 pt
Manpower invoice block
Lowwoxcn-registry
JPJordan P.2 pt

Done

3
Release please manifest bump
LowRelease
AMAlex M.1 pt
D1 migration vendor-request

Applied migrations on staging; seed verified.

MediumDatabase
SKSara K.2 pt
Storybook deploy pipeline
LowDevOps
1 pt
pnpm dlx shadcn@latest add @woxcn/kanban-board

Installs:

  • components/blocks/kanban-board.tsxKanbanBoardPage and helpers
  • lib/kanban-board-mock-data.ts — sprint mock graph (MOCK_SPRINT_BOARD, createSprintKanbanBoard)
  • lib/kanban-board-utils.ts — stats and column/card lookups
  • styles/kanban-board.css — theme overrides for react-kanban-kit
npx shadcn@latest add https://woxcn-registry.woxware.io/r/kanban-board.json
app/globals.css
@import "@/styles/kanban-board.css";

The Storybook Full board story and docs preview use the sprint mock (5 columns, 13 cards) with:

  • Drag-and-drop between columns (onBoardChange + applyCardMove)
  • Click a card → detail sheet (onCardClick)
  • Toast on move (onCardMove)
  • Toggles for viewOnly and allowColumnDrag
  • Reset mock datacreateSprintKanbanBoard()
"use client";
import { useState } from "react";
import { toast } from "sonner";
import {
KanbanBoardPage,
createSprintKanbanBoard,
getKanbanCardContent,
findKanbanColumnForCard,
type BoardData,
type BoardItem,
} from "@/components/blocks/kanban-board";
export default function SprintBoardPage() {
const [board, setBoard] = useState<BoardData>(() => createSprintKanbanBoard());
const [selected, setSelected] = useState<BoardItem | null>(null);
return (
<>
<KanbanBoardPage
board={board}
onBoardChange={setBoard}
onCardMove={(move) => {
const card = board[move.cardId];
const from = board[move.fromColumnId]?.title;
const to = board[move.toColumnId]?.title;
toast.success(`${card?.title}: ${from} → ${to}`);
}}
onCardClick={(_e, card) => setSelected(card)}
allowColumnDrag
cardsGap={10}
pageTitle="Sprint 24"
pageDescription="Drag cards, click for details, persist onBoardChange to your API."
/>
{/* Render your own Sheet / Dialog using getKanbanCardContent(selected) */}
</>
);
}
ExportDescription
MOCK_SPRINT_BOARDRead-only reference graph (do not mutate in place)
createSprintKanbanBoard()structuredClone of the mock — use as initial state
MOCK_SPRINT_BOARD_METAsprintName, sprintGoal, columnIds, cardCount
createDemoKanbanBoard()Small 3-column / 4-card board for minimal demos

Columns in the sprint mock: Backlog, To Do, In Progress, In Review, Done.

FunctionReturns
getKanbanColumnIds(board)Column ids from root.children
getKanbanColumn(board, id)Column BoardItem
getKanbanCards(board)All items with type: "card"
getKanbanCardContent(card)Typed content object
getKanbanBoardStats(board){ totalCards, totalColumns, cardsByColumn, storyPoints }
findKanbanColumnForCard(board, cardId)Parent column for a card
applyCardMove(move, board)New BoardData after a drag
<KanbanBoardPage
board={board}
onBoardChange={setBoard}
renderCard={({ data }) => (
<div className="rounded-md border bg-card p-3 text-sm shadow-sm">
{data.title}
</div>
)}
/>
<KanbanBoardPage
board={board}
onBoardChange={setBoard}
renderColumnHeader={(column, count) => (
<div className="flex justify-between px-1">
<span className="font-semibold">{column.title}</span>
<span className="text-muted-foreground text-xs">{count}</span>
</div>
)}
/>
PropTypeDefaultDescription
boardBoardDataRequired. Controlled board graph
onBoardChange(board: BoardData) => voidCalled after drag with the updated graph
onCardMove(move: DropCardParams, nextBoard: BoardData) => voidCalled on drag with move payload and next board (before onBoardChange)
onCardClick(event, card: BoardItem) => voidCard click (e.g. open detail sheet)
classNamestringRoot <section> classes
pageTitlestring"Kanban board"Heading; omit or pass "" to hide
pageDescriptionstring(see component)Subtitle; omit or pass "" to hide
viewOnlybooleanfalseDisables card drag
allowColumnDragbooleanfalseEnables column reordering
cardsGapnumber8Pixel gap between cards (library)
renderCard(props: KanbanCardRenderProps) => ReactNodeshadcn cardCustom card renderer
renderColumnHeader(column, cardCount) => ReactNodetitle + count badgeCustom column header
rootClassNamestringClasses on the library board root
type KanbanCardPriority = "low" | "medium" | "high";
interface KanbanCardContent {
description?: string;
priority?: KanbanCardPriority;
labels?: string[];
assignee?: string;
assigneeInitials?: string; // e.g. "SK" — shown in avatar chip
dueDate?: string; // ISO YYYY-MM-DD
storyPoints?: number;
}
interface BoardData {
root: BoardItem;
[id: string]: BoardItem;
}
interface BoardItem {
id: string;
title: string;
parentId: string | null;
children: string[];
totalChildrenCount: number;
type?: "card"; // cards only
content?: KanbanCardContent;
}
  • root.children — ordered column ids
  • Each column’s children — ordered card ids in that column
  • Cards use type: "card" and configMap.card in the library
interface DropCardParams {
cardId: string;
fromColumnId: string;
toColumnId: string;
taskAbove: string | null;
taskBelow: string | null;
position: number;
}

From react-kanban-kit: BoardData, BoardItem, DropCardParams.

From this block: applyCardMove, createDemoKanbanBoard, createSprintKanbanBoard, MOCK_SPRINT_BOARD, MOCK_SPRINT_BOARD_META, and board utility functions listed above.