Add React.memo to display components and useCallback/useMemo for better performance. Changes: - Added React.memo to TaskCard component - Added React.memo to StoryCard component - Added React.memo to KanbanBoard component - Added React.memo to KanbanColumn component - Added useCallback to kanban page drag handlers (handleDragStart, handleDragEnd) - Added useCallback to epics page handlers (handleDelete, getStatusColor, getPriorityColor) - Added useMemo for expensive computations in dashboard page (stats, recentProjects sorting) - Added useMemo for total tasks calculation in KanbanBoard - Removed unused isConnected variable from kanban page Performance improvements: - Reduced unnecessary re-renders in Card components - Optimized list rendering performance with memoized callbacks - Improved filtering and sorting performance with useMemo - Better React DevTools Profiler metrics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { useDroppable } from '@dnd-kit/core';
|
|
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Story } from '@/types/project';
|
|
import { StoryCard } from './StoryCard';
|
|
|
|
interface KanbanColumnProps {
|
|
id: string;
|
|
title: string;
|
|
stories: Story[];
|
|
epicNames?: Record<string, string>; // Map of epicId -> epicName
|
|
taskCounts?: Record<string, number>; // Map of storyId -> taskCount
|
|
}
|
|
|
|
export const KanbanColumn = React.memo(function KanbanColumn({ id, title, stories, epicNames = {}, taskCounts = {} }: 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">{stories.length}</span>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent ref={setNodeRef} className="space-y-2 min-h-[400px]">
|
|
<SortableContext
|
|
items={stories.map((s) => s.id)}
|
|
strategy={verticalListSortingStrategy}
|
|
>
|
|
{stories.map((story) => (
|
|
<StoryCard
|
|
key={story.id}
|
|
story={story}
|
|
epicName={epicNames[story.epicId]}
|
|
taskCount={taskCounts[story.id]}
|
|
/>
|
|
))}
|
|
</SortableContext>
|
|
{stories.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 stories</p>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
});
|