feat(frontend): Implement Issue management and Kanban board
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>
This commit is contained in:
58
components/features/kanban/IssueCard.tsx
Normal file
58
components/features/kanban/IssueCard.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Issue } from '@/lib/api/issues';
|
||||
|
||||
interface IssueCardProps {
|
||||
issue: Issue;
|
||||
}
|
||||
|
||||
export function IssueCard({ issue }: IssueCardProps) {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||
useSortable({ id: issue.id });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
const priorityColors = {
|
||||
Low: 'bg-gray-100 text-gray-700',
|
||||
Medium: 'bg-blue-100 text-blue-700',
|
||||
High: 'bg-orange-100 text-orange-700',
|
||||
Critical: 'bg-red-100 text-red-700',
|
||||
};
|
||||
|
||||
const typeIcons = {
|
||||
Story: '📖',
|
||||
Task: '✓',
|
||||
Bug: '🐛',
|
||||
Epic: '🚀',
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="cursor-grab active:cursor-grabbing hover:shadow-md transition-shadow"
|
||||
>
|
||||
<CardContent className="p-3 space-y-2">
|
||||
<div className="flex items-start gap-2">
|
||||
<span>{typeIcons[issue.type]}</span>
|
||||
<h3 className="text-sm font-medium flex-1">{issue.title}</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className={priorityColors[issue.priority]}>
|
||||
{issue.priority}
|
||||
</Badge>
|
||||
<Badge variant="secondary">{issue.type}</Badge>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user