KanbanBoard
kanbanboard-1779379195813.tsx
'use client'
import { useState } from 'react'
type Priority = 'high' | 'medium' | 'low'
type Status = 'todo' | 'in-progress' | 'review' | 'done'
interface Card {
id: number
title: string
priority: Priority
assignee: string
tags: string[]
}
const PRIORITY_COLORS = { high: '#ef4444', medium: '#f59e0b', low: '#22c55e' }
const INITIAL: Record<Status, Card[]> = {
'todo': [
{ id: 1, title: 'Design pricing page', priority: 'high', assignee: 'SC', tags: ['design', 'frontend'] },
{ id: 2, title: 'Setup pgvector index', priority: 'medium', assignee: 'MW', tags: ['backend', 'db'] },
{ id: 3, title: 'Write MCP docs', priority: 'low', assignee: 'PP', tags: ['docs'] },
],
'in-progress': [
{ id: 4, title: 'User auth system', priority: 'high', assignee: 'AK', tags: ['backend', 'auth'] },
{ id: 5, title: 'Component catalogue UI', priority: 'medium', assignee: 'SC', tags: ['frontend'] },
],
'review': [
{ id: 6, title: 'Admin panel CRUD', priority: 'medium', assignee: 'MW', tags: ['frontend', 'admin'] },
],
'done': [
{ id: 7, title: 'Wave 1 generation (20 components)', priority: 'high', assignee: 'AI', tags: ['generation'] },
{ id: 8, title: 'Docker deployment setup', priority: 'high', assignee: 'AK', tags: ['devops'] },
],
}
const COLS: { id: Status; label: string; color: string }[] = [
{ id: 'todo', label: 'To Do', color: '#64748b' },
{ id: 'in-progress', label: 'In Progress', color: '#C9A84C' },
{ id: 'review', label: 'Review', color: '#6366f1' },
{ id: 'done', label: 'Done', color: '#22c55e' },
]
const AVATAR_COLORS: Record<string, string> = { SC: '#C9A84C', MW: '#6366f1', PP: '#22c55e', AK: '#ef4444', AI: '#f59e0b' }
export default function KanbanBoard() {
const [board, setBoard] = useState(INITIAL)
const [dragging, setDragging] = useState<{ card: Card; from: Status } | null>(null)
function onDrop(to: Status) {
if (!dragging || dragging.from === to) return
setBoard(b => {
const next = { ...b }
next[dragging.from] = next[dragging.from].filter(c => c.id !== dragging.card.id)
next[to] = [...next[to], dragging.card]
return next
})
setDragging(null)
}
return (
<div style={{ background: '#080808', padding: 20, overflowX: 'auto' }}>
<div style={{ display: 'flex', gap: 12, minWidth: 680 }}>
{COLS.map(col => (
<div key={col.id} onDragOver={e => e.preventDefault()} onDrop={() => onDrop(col.id)}
style={{ flex: 1, background: '#0F0F0F', border: `1px solid ${dragging ? 'rgba(201,168,76,0.2)' : 'rgba(255,255,255,0.06)'}`, borderRadius: 14, overflow: 'hidden', transition: 'border-color 0.2s', minWidth: 160 }}>
<div style={{ padding: '12px 14px', borderBottom: '1px solid rgba(255,255,255,0.06)', display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ width: 8, height: 8, borderRadius: '50%', background: col.color }} />
<span style={{ color: '#F5F5F0', fontSize: 13, fontWeight: 600 }}>{col.label}</span>
<span style={{ marginLeft: 'auto', background: 'rgba(255,255,255,0.08)', color: 'rgba(255,255,255,0.4)', borderRadius: 20, padding: '1px 7px', fontSize: 11 }}>{board[col.id].length}</span>
</div>
<div style={{ padding: 10, display: 'flex', flexDirection: 'column', gap: 8, minHeight: 200 }}>
{board[col.id].map(card => (
<div key={card.id} draggable onDragStart={() => setDragging({ card, from: col.id })} onDragEnd={() => setDragging(null)}
style={{ background: '#1a1a1a', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 10, padding: 12, cursor: 'grab', opacity: dragging?.card.id === card.id ? 0.5 : 1, transition: 'opacity 0.2s' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
<span style={{ color: '#F5F5F0', fontSize: 13, fontWeight: 500, lineHeight: 1.4 }}>{card.title}</span>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: PRIORITY_COLORS[card.priority], flexShrink: 0, marginTop: 4, marginLeft: 6 }} />
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
{card.tags.map(tag => (
<span key={tag} style={{ background: 'rgba(255,255,255,0.06)', color: 'rgba(255,255,255,0.4)', padding: '1px 6px', borderRadius: 4, fontSize: 10 }}>{tag}</span>
))}
</div>
<div style={{ width: 22, height: 22, borderRadius: '50%', background: AVATAR_COLORS[card.assignee] || '#555', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#0A0A0A', fontSize: 9, fontWeight: 800, flexShrink: 0 }}>{card.assignee}</div>
</div>
</div>
))}
</div>
</div>
))}
</div>
</div>
)
}Component info
CategoryProject Management
Frameworkreact
TierFREE
Views0
Copies0
About
Drag-and-drop Kanban board with swimlanes, card priorities, assignees, and task counts