feat(frontend): Refactor Kanban board to focus on Story management

Refactored the Kanban board from a mixed Epic/Story/Task view to focus exclusively on Stories, which are the right granularity for Kanban management.

Changes:
- Created StoryCard component with Epic breadcrumb, priority badges, and estimated hours display
- Updated KanbanColumn to use Story type and display epic names
- Created CreateStoryDialog for story creation with epic selection
- Added useProjectStories hook to fetch all stories across epics for a project
- Refactored Kanban page to show Stories only with drag-and-drop status updates
- Updated SignalR event handlers to focus on Story events only
- Changed UI text from 'New Issue' to 'New Story' and 'update issue status' to 'update story status'
- Implemented story status change via useChangeStoryStatus hook

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-05 15:03:12 +01:00
parent 2a0394b5ab
commit 90e3d2416c
6 changed files with 493 additions and 127 deletions

View File

@@ -3,16 +3,18 @@
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';
import { Story } from '@/types/project';
import { StoryCard } from './StoryCard';
interface KanbanColumnProps {
id: string;
title: string;
issues: Issue[];
stories: Story[];
epicNames?: Record<string, string>; // Map of epicId -> epicName
taskCounts?: Record<string, number>; // Map of storyId -> taskCount
}
export function KanbanColumn({ id, title, issues }: KanbanColumnProps) {
export function KanbanColumn({ id, title, stories, epicNames = {}, taskCounts = {} }: KanbanColumnProps) {
const { setNodeRef } = useDroppable({ id });
return (
@@ -20,21 +22,26 @@ export function KanbanColumn({ id, title, issues }: KanbanColumnProps) {
<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>
<span className="text-muted-foreground">{stories.length}</span>
</CardTitle>
</CardHeader>
<CardContent ref={setNodeRef} className="space-y-2 min-h-[400px]">
<SortableContext
items={issues.map((i) => i.id)}
items={stories.map((s) => s.id)}
strategy={verticalListSortingStrategy}
>
{issues.map((issue) => (
<IssueCard key={issue.id} issue={issue} />
{stories.map((story) => (
<StoryCard
key={story.id}
story={story}
epicName={epicNames[story.epicId]}
taskCount={taskCounts[story.id]}
/>
))}
</SortableContext>
{issues.length === 0 && (
{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 issues</p>
<p className="text-sm text-muted-foreground">No stories</p>
</div>
)}
</CardContent>