'use client'; import React from 'react'; 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 { KanbanItem, isKanbanEpic, isKanbanStory, isKanbanTask, getKanbanItemTitle } from '@/types/kanban'; import { FolderKanban, FileText, CheckSquare } from 'lucide-react'; interface IssueCardProps { issue: KanbanItem; } export const IssueCard = React.memo(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', }; // Type icon components - type-safe with discriminated union const getTypeIcon = () => { switch (issue.type) { case 'Epic': return ; case 'Story': return ; case 'Task': return ; default: return null; } }; // Parent breadcrumb (for Story and Task) - type-safe with type guards const renderParentBreadcrumb = () => { // Story shows parent Epic - TypeScript knows epicId exists if (isKanbanStory(issue)) { return (
Epic: {issue.epicId}
); } // Task shows parent Story - TypeScript knows storyId exists if (isKanbanTask(issue)) { return (
Story: {issue.storyId}
); } return null; }; // Child count badge (for Epic and Story) - type-safe with type guards const renderChildCount = () => { // Epic shows number of stories - TypeScript knows childCount exists if (isKanbanEpic(issue) && issue.childCount && issue.childCount > 0) { return ( {issue.childCount} stories ); } // Story shows number of tasks - TypeScript knows childCount exists if (isKanbanStory(issue) && issue.childCount && issue.childCount > 0) { return ( {issue.childCount} tasks ); } return null; }; // Get display title - type-safe helper function const displayTitle = getKanbanItemTitle(issue); return ( {/* Header: Type icon + Child count */}
{getTypeIcon()} {issue.type}
{renderChildCount()}
{/* Parent breadcrumb */} {renderParentBreadcrumb()} {/* Title - type-safe */}

{displayTitle}

{/* Description (if available) - type-safe */} {issue.description && (

{issue.description}

)} {/* Footer: Priority + Hours - type-safe */}
{issue.priority} {issue.estimatedHours && ( {issue.estimatedHours}h )}
); });