AI
Kanban logov1.0.0

Kanban Production Examples

Below are production-ready, copy-paste implementations of Nofinite Kanban showcasing both Framework-Agnostic Core and React integrations.


Example 1: Vanilla TypeScript Real-Time Kanban Board

Direct DOM mounting with real-time external card additions, search filtering, and state event handling without React:

typescript
import { createKanbanBoard, type KanbanColumn, type CardMoveEvent } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';

// 1. Initial sprint column data
const sprintColumns: KanbanColumn[] = [
  {
    id: 'backlog',
    title: 'Sprint Backlog',
    items: [
      {
        id: 'eng-101',
        title: 'Upgrade Node.js to v22 LTS in Docker base image',
        badge: { text: 'DevOps', variant: 'primary' },
        assignee: { name: 'Alex Rivera' },
        subtasks: { total: 3, completed: 0 },
        dueDate: 'Oct 20'
      }
    ]
  },
  {
    id: 'in-progress',
    title: 'In Progress',
    wipLimit: 2, // Hard limit of 2 active cards
    items: [
      {
        id: 'eng-103',
        title: 'Optimize SVG path calculation for multi-series charts',
        badge: { text: 'Performance', variant: 'warning' },
        assignee: { name: 'Marcus Vance' },
        subtasks: { total: 4, completed: 3 },
        dueDate: 'Tomorrow'
      }
    ]
  },
  {
    id: 'done',
    title: 'Completed',
    items: []
  }
];

// 2. Mount board directly into container
const container = document.getElementById('kanban-app')!;
const board = createKanbanBoard(container, {
  columns: sprintColumns,
  enforceWipLimit: true,
  onCardMove: (event: CardMoveEvent) => {
    console.log(`[Telemetry] Moved card ${event.cardId} to column ${event.targetColumnId}`);
    // Sync with backend API:
    // fetch('/api/tasks/move', { method: 'POST', body: JSON.stringify(event) });
  }
});

// 3. Connect external search input
document.getElementById('task-search')?.addEventListener('input', (e) => {
  const query = (e.target as HTMLInputElement).value;
  board.setFilter({ query });
});

// 4. Connect external Quick Add button
document.getElementById('quick-add-btn')?.addEventListener('click', () => {
  board.addCard('backlog', {
    id: `eng-${Date.now()}`,
    title: 'New Incoming Ticket',
    badge: { text: 'Triage', variant: 'default' }
  });
});

Example 2: React Agile Engineering Sprint Board

Features strict Work-in-Progress (WIP) limits, priority badges, story point counters, assignee avatars, and interactive subtasks:

tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';

const sprintColumns: KanbanColumnProps[] = [
  {
    id: 'backlog',
    title: 'Sprint Backlog',
    items: [
      {
        id: 'eng-101',
        title: 'Upgrade Node.js to v22 LTS in Docker base image',
        badge: { text: 'DevOps', variant: 'primary' },
        assignee: {
          name: 'Alex Rivera',
          avatarUrl: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100',
        },
        subtasks: { total: 3, completed: 0 },
        dueDate: 'Oct 20',
      },
    ],
  },
  {
    id: 'in-progress',
    title: 'In Progress',
    wipLimit: 2, // Hard limit of 2 active cards
    items: [
      {
        id: 'eng-103',
        title: 'Optimize SVG path calculation for multi-series charts',
        badge: { text: 'Performance', variant: 'warning' },
        assignee: {
          name: 'Marcus Vance',
          avatarUrl: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',
        },
        subtasks: { total: 4, completed: 3 },
        dueDate: 'Tomorrow',
      },
    ],
  },
  {
    id: 'done',
    title: 'Completed',
    items: [],
  },
];

export function AgileSprintBoard() {
  const [columns, setColumns] = useState(sprintColumns);

  return (
    <div className="w-full h-[650px] p-6 bg-surface">
      <Kanban
        columns={columns}
        onChange={setColumns}
        enforceWipLimit={true}
        onCardMove={(e) => console.log('Card moved in React:', e)}
      />
    </div>
  );
}