import React, { useState, useCallback, ReactNode, DragEvent } from 'react' import { MoreHorizontal, Plus, GripVertical } from 'lucide-react' // ============================================================================ // Types // ============================================================================ export interface KanbanItem { id: string title: string description?: string columnId: string [key: string]: unknown } export interface KanbanColumn { id: string title: string color?: string } // ============================================================================ // KanbanBoard - Main container // ============================================================================ interface KanbanBoardProps { columns: KanbanColumn[] items: T[] onMoveItem: (itemId: string, toColumnId: string) => void onAddItem?: (columnId: string) => void renderItem?: (item: T) => ReactNode className?: string } export function KanbanBoard({ columns, items, onMoveItem, onAddItem, renderItem, className = '', }: KanbanBoardProps) { const [draggedItem, setDraggedItem] = useState(null) const [dragOverColumn, setDragOverColumn] = useState(null) const handleDragStart = useCallback((itemId: string) => { setDraggedItem(itemId) }, []) const handleDragEnd = useCallback(() => { setDraggedItem(null) setDragOverColumn(null) }, []) const handleDragOver = useCallback((e: DragEvent, columnId: string) => { e.preventDefault() setDragOverColumn(columnId) }, []) const handleDrop = useCallback((e: DragEvent, columnId: string) => { e.preventDefault() if (draggedItem) { onMoveItem(draggedItem, columnId) } setDraggedItem(null) setDragOverColumn(null) }, [draggedItem, onMoveItem]) return (
{columns.map((column) => { const columnItems = items.filter((item) => item.columnId === column.id) const isDragOver = dragOverColumn === column.id return ( handleDragOver(e, column.id)} onDrop={(e) => handleDrop(e, column.id)} onDragLeave={() => setDragOverColumn(null)} onAddItem={onAddItem ? () => onAddItem(column.id) : undefined} > {columnItems.map((item) => ( handleDragStart(item.id)} onDragEnd={handleDragEnd} renderContent={renderItem} /> ))} ) })}
) } // ============================================================================ // KanbanColumn // ============================================================================ interface KanbanColumnComponentProps { column: KanbanColumn items: T[] children: ReactNode isDragOver: boolean onDragOver: (e: DragEvent) => void onDrop: (e: DragEvent) => void onDragLeave: () => void onAddItem?: () => void } function KanbanColumnComponent({ column, items, children, isDragOver, onDragOver, onDrop, onDragLeave, onAddItem, }: KanbanColumnComponentProps) { return (
{/* Header */}
{column.color && ( )}

{column.title}

{items.length}
{/* Items */}
{children}
{/* Add button */} {onAddItem && ( )}
) } // ============================================================================ // KanbanCard // ============================================================================ interface KanbanCardProps { item: T isDragging: boolean onDragStart: () => void onDragEnd: () => void renderContent?: (item: T) => ReactNode } function KanbanCard({ item, isDragging, onDragStart, onDragEnd, renderContent, }: KanbanCardProps) { return (
{renderContent ? ( renderContent(item) ) : ( <>

{item.title}

{item.description && (

{item.description}

)}
)}
) } // ============================================================================ // useKanban - Hook for managing kanban state // ============================================================================ interface UseKanbanOptions { initialItems: T[] onItemMoved?: (item: T, fromColumn: string, toColumn: string) => void } interface UseKanbanReturn { items: T[] moveItem: (itemId: string, toColumnId: string) => void addItem: (item: T) => void updateItem: (itemId: string, updates: Partial) => void removeItem: (itemId: string) => void } export function useKanban({ initialItems, onItemMoved, }: UseKanbanOptions): UseKanbanReturn { const [items, setItems] = useState(initialItems) const moveItem = useCallback((itemId: string, toColumnId: string) => { setItems((prev) => { const item = prev.find((i) => i.id === itemId) if (!item || item.columnId === toColumnId) return prev const fromColumn = item.columnId const updated = prev.map((i) => i.id === itemId ? { ...i, columnId: toColumnId } : i ) onItemMoved?.({ ...item, columnId: toColumnId } as T, fromColumn, toColumnId) return updated }) }, [onItemMoved]) const addItem = useCallback((item: T) => { setItems((prev) => [...prev, item]) }, []) const updateItem = useCallback((itemId: string, updates: Partial) => { setItems((prev) => prev.map((i) => (i.id === itemId ? { ...i, ...updates } : i)) ) }, []) const removeItem = useCallback((itemId: string) => { setItems((prev) => prev.filter((i) => i.id !== itemId)) }, []) return { items, moveItem, addItem, updateItem, removeItem } } export default KanbanBoard