Add comprehensive Issue management functionality with drag-and-drop Kanban board. Changes: - Created Issue API client (issues.ts) with CRUD operations - Implemented React Query hooks for Issue data management - Added IssueCard component with drag-and-drop support using @dnd-kit - Created KanbanColumn component with droppable zones - Built CreateIssueDialog with form validation using zod - Implemented Kanban page at /projects/[id]/kanban with DnD status changes - Added missing UI components (textarea, select, skeleton) - Enhanced API client with helper methods (get, post, put, patch, delete) - Installed dependencies: @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities, @radix-ui/react-select, sonner - Fixed SignalR ConnectionManager TypeScript error - Preserved legacy KanbanBoard component for backward compatibility Features: - Drag and drop issues between Backlog, Todo, InProgress, and Done columns - Real-time status updates via API - Issue creation with type (Story, Task, Bug, Epic) and priority - Visual feedback with priority colors and type icons - Toast notifications for user actions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
'use client';
|
|
|
|
import { useDroppable } from '@dnd-kit/core';
|
|
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Issue } from '@/lib/api/issues';
|
|
import { IssueCard } from './IssueCard';
|
|
|
|
interface KanbanColumnProps {
|
|
id: string;
|
|
title: string;
|
|
issues: Issue[];
|
|
}
|
|
|
|
export function KanbanColumn({ id, title, issues }: KanbanColumnProps) {
|
|
const { setNodeRef } = useDroppable({ id });
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-sm font-medium flex items-center justify-between">
|
|
<span>{title}</span>
|
|
<span className="text-muted-foreground">{issues.length}</span>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent ref={setNodeRef} className="space-y-2 min-h-[400px]">
|
|
<SortableContext
|
|
items={issues.map((i) => i.id)}
|
|
strategy={verticalListSortingStrategy}
|
|
>
|
|
{issues.map((issue) => (
|
|
<IssueCard key={issue.id} issue={issue} />
|
|
))}
|
|
</SortableContext>
|
|
{issues.length === 0 && (
|
|
<div className="flex h-32 items-center justify-center rounded-lg border-2 border-dashed border-muted-foreground/25">
|
|
<p className="text-sm text-muted-foreground">No issues</p>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|