diff --git a/docs/plans/sprint_4_story_1.md b/docs/plans/sprint_4_story_1.md
new file mode 100644
index 0000000..4e3669e
--- /dev/null
+++ b/docs/plans/sprint_4_story_1.md
@@ -0,0 +1,223 @@
+---
+story_id: sprint_4_story_1
+sprint: sprint_4
+priority: P0
+status: not_started
+story_points: 5
+estimated_days: 3
+created_date: 2025-11-05
+assignee: Frontend Team
+---
+
+# Story 1: Story Detail Page Foundation
+
+**Sprint**: Sprint 4
+**Priority**: P0 (Critical)
+**Estimated**: 3 days
+**Owner**: Frontend Team
+
+## Description
+
+Create the Story detail page (`/stories/[id]`) to fix the critical 404 error when users click on Story cards. This page will display comprehensive Story information including title, description, status, priority, metadata, and parent Epic context using a two-column layout consistent with the existing Epic detail page.
+
+## User Story
+
+**As a** project manager or developer,
+**I want** to view detailed Story information by clicking on a Story card,
+**So that** I can see the full Story description, acceptance criteria, Tasks, and manage Story metadata.
+
+## Acceptance Criteria
+
+- [ ] Clicking Story card from Epic detail page navigates to `/stories/{id}` page (no 404 error)
+- [ ] Page displays Story title, description, status, priority, and all metadata
+- [ ] Two-column layout with main content area and metadata sidebar
+- [ ] Breadcrumb navigation shows: Projects > Project > Epics > Epic > Stories > Story
+- [ ] Metadata sidebar shows status, priority, assignee, time tracking, dates, and parent Epic card
+- [ ] Edit button opens Story form dialog with current Story data
+- [ ] Delete button shows confirmation dialog and removes Story
+- [ ] Loading states display skeleton loaders
+- [ ] Error states handle 404, network errors, and permission errors
+- [ ] Responsive design works on mobile, tablet, and desktop
+- [ ] Back button (or ESC key) navigates to parent Epic detail page
+
+## Technical Requirements
+
+**New Route**:
+- `app/(dashboard)/stories/[id]/page.tsx` - Story detail page (400-500 lines)
+
+**New Components**:
+- `components/projects/story-header.tsx` - Story page header (100-150 lines)
+- `components/projects/story-metadata-sidebar.tsx` - Story sidebar (150-200 lines)
+
+**Reuse Pattern**: 85% similar to Epic detail page
+- Reference: `app/(dashboard)/epics/[id]/page.tsx` (534 lines)
+- Adapt Epic structure for Story context
+- Replace Stories list with Tasks list (Story 2 dependency)
+
+**API Integration**:
+- Use `useStory(id)` hook (already exists)
+- Use `useUpdateStory()` hook for Edit action
+- Use `useDeleteStory()` hook for Delete action
+
+**Layout Structure**:
+```
+┌────────────────────────────────────────────────────────┐
+│ [Breadcrumb Navigation] [Actions] │
+├────────────────────────────────────────────────────────┤
+│ [←] Story Title [Edit][Delete] │
+│ [Status Badge] [Priority Badge] │
+├────────────────────────────────────────────────────────┤
+│ ┌────────────────────────┐ ┌──────────────────────┐ │
+│ │ Main Content │ │ Metadata Sidebar │ │
+│ │ - Description │ │ - Status │ │
+│ │ - Acceptance Criteria │ │ - Priority │ │
+│ │ - Tasks (Story 2) │ │ - Assignee │ │
+│ │ │ │ - Time Tracking │ │
+│ │ │ │ - Dates │ │
+│ │ │ │ - Parent Epic Card │ │
+│ └────────────────────────┘ └──────────────────────┘ │
+└────────────────────────────────────────────────────────┘
+```
+
+## Tasks
+
+- [ ] [Task 1](sprint_4_story_1_task_1.md) - Create Story detail page route and layout
+- [ ] [Task 2](sprint_4_story_1_task_2.md) - Implement Story header component
+- [ ] [Task 3](sprint_4_story_1_task_3.md) - Implement Story metadata sidebar component
+- [ ] [Task 4](sprint_4_story_1_task_4.md) - Integrate Story API data loading and error handling
+- [ ] [Task 5](sprint_4_story_1_task_5.md) - Add Edit and Delete actions with dialogs
+- [ ] [Task 6](sprint_4_story_1_task_6.md) - Implement responsive design and accessibility
+
+**Progress**: 0/6 tasks completed
+
+## Dependencies
+
+**Prerequisites**:
+- ✅ Story API ready (`GET /api/v1/stories/{id}`)
+- ✅ `useStory(id)` hook implemented (`lib/hooks/use-stories.ts`)
+- ✅ Story types defined (`types/project.ts`)
+- ✅ Story form component exists (`components/projects/story-form.tsx`)
+- ✅ Epic detail page as reference pattern
+
+**Blocked By**: None (can start immediately)
+
+**Blocks**:
+- Story 2 (Task Management) - depends on Story detail page existing
+
+## Definition of Done
+
+- All 6 tasks completed
+- Story detail page accessible at `/stories/{id}` route
+- Page displays all Story information correctly
+- Layout consistent with Epic detail page
+- Edit and Delete actions working
+- Loading and error states implemented
+- Responsive design tested on all breakpoints
+- Accessibility requirements met (keyboard navigation, ARIA labels)
+- Code reviewed and approved
+- Git commit created with descriptive message
+
+## Test Plan
+
+**Manual Testing**:
+1. Navigate to Epic detail page
+2. Click any Story card → Verify navigates to `/stories/{id}` (no 404)
+3. Verify Story title, description, status, priority display correctly
+4. Verify breadcrumb navigation shows full hierarchy
+5. Verify metadata sidebar shows assignee, time, dates, parent Epic
+6. Click Edit → Verify form opens with Story data
+7. Edit Story → Verify changes saved and reflected
+8. Click Delete → Verify confirmation dialog appears
+9. Delete Story → Verify redirected to Epic detail page
+10. Test invalid Story ID → Verify 404 error page
+11. Test mobile/tablet/desktop responsive layouts
+12. Test keyboard navigation (Tab, Enter, ESC)
+
+**E2E Test** (Playwright):
+```typescript
+test('user can view story details', async ({ page }) => {
+ await page.goto('/epics/epic-123');
+ await page.click('[data-testid="story-card"]');
+ await expect(page).toHaveURL(/\/stories\/story-\w+/);
+ await expect(page.locator('h1')).toContainText('Story Title');
+ await expect(page.locator('[data-testid="story-status"]')).toBeVisible();
+});
+```
+
+**Verification Commands**:
+```bash
+# Check route exists
+ls app/(dashboard)/stories/[id]/page.tsx
+
+# Run dev server and test
+npm run dev
+# Navigate to http://localhost:3000/stories/[valid-id]
+
+# Build for production
+npm run build
+# Verify no build errors
+```
+
+## UX Design Reference
+
+**Design Document**: `docs/designs/STORY_UX_UI_DESIGN.md`
+- Section: "Story Detail Page Design" (lines 117-164)
+- Layout: Two-column (main content + metadata sidebar)
+- Breadcrumb: Projects > Project > Epics > Epic > Stories > Story
+- Header: Title, Status, Priority, Edit, Delete actions
+- Sidebar: Metadata fields and parent Epic card
+
+**Design Tokens**:
+```css
+/* Status Colors */
+Backlog: bg-slate-100 text-slate-700
+Todo: bg-blue-100 text-blue-700
+InProgress: bg-amber-100 text-amber-700
+Done: bg-green-100 text-green-700
+
+/* Priority Colors */
+Low: bg-blue-100 text-blue-700
+Medium: bg-yellow-100 text-yellow-700
+High: bg-orange-100 text-orange-700
+Critical: bg-red-100 text-red-700
+
+/* Typography */
+Story Title: 32px, Bold, Line-height 1.2
+Story Description: 16px, Regular, Line-height 1.6
+Metadata Label: 14px, Medium
+Metadata Value: 14px, Regular
+```
+
+## Notes
+
+**Why This Matters**:
+- **Critical Bug**: Users currently see 404 error when clicking Story cards
+- **User Experience**: Enables Epic → Story → Task navigation
+- **Foundation**: Required for Story 2 (Task Management)
+- **Consistency**: Maintains design patterns established by Epic detail page
+
+**Code Reuse Strategy**:
+- Copy `app/(dashboard)/epics/[id]/page.tsx` structure
+- Replace `useEpic` → `useStory`
+- Replace `useStories` → `useTasks` (Story 2)
+- Update breadcrumb (add Story level)
+- Adapt sidebar (Parent Project → Parent Epic)
+- 85% code reuse, 50-60% faster development
+
+**Performance Targets**:
+- Page load time: < 1 second
+- Time to interactive: < 2 seconds
+- Lighthouse score: >= 90
+- No layout shift (CLS < 0.1)
+
+**Accessibility Requirements** (WCAG 2.1 Level AA):
+- Keyboard navigation: All interactive elements
+- Focus indicators: 2px solid outline
+- ARIA labels: All buttons and links
+- Screen reader: Proper heading hierarchy (h1 → h2 → h3)
+- Color contrast: 4.5:1 minimum for text
+
+---
+
+**Created**: 2025-11-05 by Frontend Agent
+**Updated**: 2025-11-05
diff --git a/docs/plans/sprint_4_story_1_task_1.md b/docs/plans/sprint_4_story_1_task_1.md
new file mode 100644
index 0000000..b7d79b7
--- /dev/null
+++ b/docs/plans/sprint_4_story_1_task_1.md
@@ -0,0 +1,152 @@
+---
+task_id: sprint_4_story_1_task_1
+story_id: sprint_4_story_1
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 1
+created_date: 2025-11-05
+estimated_hours: 4
+---
+
+# Task 1: Create Story Detail Page Route and Layout
+
+## Description
+
+Create the Story detail page route at `app/(dashboard)/stories/[id]/page.tsx` with a two-column layout structure. This task establishes the foundational page structure that will be enhanced with components in subsequent tasks.
+
+## What to Do
+
+1. Create new directory: `app/(dashboard)/stories/[id]/`
+2. Create page component: `page.tsx`
+3. Copy layout structure from Epic detail page (`app/(dashboard)/epics/[id]/page.tsx`)
+4. Implement two-column grid layout (main content + sidebar)
+5. Add basic page wrapper and container
+6. Set up TypeScript types and props
+7. Test route accessibility
+
+**Key Changes from Epic Page**:
+- Route: `/stories/[id]` instead of `/epics/[id]`
+- Page title: "Story Detail" instead of "Epic Detail"
+- Breadcrumb: Add Story level in hierarchy
+- Prepare sections: Description, Acceptance Criteria (placeholder), Tasks (placeholder for Story 2)
+
+## Files to Modify/Create
+
+- `app/(dashboard)/stories/[id]/page.tsx` (new, ~100 lines initial structure)
+
+## Implementation Details
+
+```typescript
+// app/(dashboard)/stories/[id]/page.tsx
+import { Suspense } from 'react';
+import { notFound } from 'next/navigation';
+import { Card } from '@/components/ui/card';
+import { Skeleton } from '@/components/ui/skeleton';
+
+interface StoryDetailPageProps {
+ params: { id: string };
+}
+
+export default function StoryDetailPage({ params }: StoryDetailPageProps) {
+ return (
+
+ {/* Breadcrumb Navigation - Task 1 */}
+
+ {/* Placeholder for breadcrumb */}
+
+
+ {/* Story Header - Task 2 */}
+
+ {/* Placeholder for story header */}
+
+
+ {/* Two-column layout */}
+
+ {/* Main Content Column */}
+
+ {/* Story Description Card */}
+
+ {/* Placeholder for description - Task 4 */}
+
+
+ {/* Acceptance Criteria Card */}
+
+ {/* Placeholder for acceptance criteria - Story 3 */}
+
+
+ {/* Tasks Section - Story 2 dependency */}
+
+ {/* Placeholder for tasks list - Story 2 */}
+
+
+
+ {/* Metadata Sidebar Column - Task 3 */}
+
+
+
+ );
+}
+
+// Loading state
+export function StoryDetailPageSkeleton() {
+ return (
+
+ );
+}
+```
+
+## Acceptance Criteria
+
+- [ ] Route `/stories/[id]` is accessible (no 404 error)
+- [ ] Page renders with two-column layout on desktop
+- [ ] Layout is responsive (single column on mobile)
+- [ ] TypeScript types are properly defined
+- [ ] No console errors or warnings
+- [ ] Loading skeleton displays during data fetch
+
+## Testing
+
+```bash
+# Start dev server
+npm run dev
+
+# Test route
+# Navigate to: http://localhost:3000/stories/[any-valid-story-id]
+# Should see basic page structure (not 404)
+
+# Test responsive layout
+# Resize browser window
+# Desktop (> 1024px): Two columns
+# Mobile (< 1024px): Single column
+```
+
+## Dependencies
+
+**Prerequisites**:
+- ✅ Next.js 15 app directory structure
+- ✅ shadcn/ui Card and Skeleton components
+
+**Blocks**:
+- Task 2, 3, 4, 5, 6 (all depend on page structure existing)
+
+## Estimated Time
+
+4 hours
+
+## Notes
+
+This task creates the "skeleton" of the Story detail page. Subsequent tasks will fill in the components and functionality. Keep the structure simple and focus on layout correctness.
diff --git a/docs/plans/sprint_4_story_1_task_2.md b/docs/plans/sprint_4_story_1_task_2.md
new file mode 100644
index 0000000..f8ecfa1
--- /dev/null
+++ b/docs/plans/sprint_4_story_1_task_2.md
@@ -0,0 +1,241 @@
+---
+task_id: sprint_4_story_1_task_2
+story_id: sprint_4_story_1
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 1
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 2: Implement Story Header Component
+
+## Description
+
+Create the Story header component that displays the Story title, status badge, priority badge, and action buttons (Edit, Delete). This header sits at the top of the Story detail page and includes a back button for navigation.
+
+## What to Do
+
+1. Create `components/projects/story-header.tsx`
+2. Display Story title (32px, bold)
+3. Add back button (navigates to parent Epic)
+4. Display status badge with color coding
+5. Display priority badge with color coding
+6. Add Edit button (opens Story form dialog)
+7. Add Delete button (opens confirmation dialog)
+8. Implement responsive layout for mobile
+9. Add keyboard navigation (ESC key for back)
+
+## Files to Create/Modify
+
+- `components/projects/story-header.tsx` (new, ~100-150 lines)
+- `app/(dashboard)/stories/[id]/page.tsx` (modify, integrate header)
+
+## Implementation Details
+
+```typescript
+// components/projects/story-header.tsx
+'use client';
+
+import { useRouter } from 'next/navigation';
+import { ArrowLeft, Edit, Trash2 } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import type { Story } from '@/types/project';
+
+interface StoryHeaderProps {
+ story: Story;
+ onEdit: () => void;
+ onDelete: () => void;
+}
+
+export function StoryHeader({ story, onEdit, onDelete }: StoryHeaderProps) {
+ const router = useRouter();
+
+ const handleBack = () => {
+ router.push(`/epics/${story.epicId}`);
+ };
+
+ // Keyboard shortcut: ESC to go back
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') {
+ handleBack();
+ }
+ };
+ window.addEventListener('keydown', handleKeyDown);
+ return () => window.removeEventListener('keydown', handleKeyDown);
+ }, []);
+
+ return (
+
+
+ {/* Back Button */}
+
+
+ {/* Title and Badges */}
+
+
+ {story.title}
+
+
+
+ {/* Status Badge */}
+
+ {story.status}
+
+
+ {/* Priority Badge */}
+
+ {story.priority}
+
+
+
+
+
+ {/* Action Buttons */}
+
+
+
+
+
+
+ );
+}
+
+// Helper functions for badge variants
+function getStatusVariant(status: string) {
+ const variants = {
+ Backlog: 'secondary',
+ Todo: 'default',
+ InProgress: 'warning',
+ Done: 'success',
+ };
+ return variants[status] || 'default';
+}
+
+function getPriorityVariant(priority: string) {
+ const variants = {
+ Low: 'default',
+ Medium: 'warning',
+ High: 'destructive',
+ Critical: 'destructive',
+ };
+ return variants[priority] || 'default';
+}
+```
+
+**Integration in page.tsx**:
+```typescript
+// app/(dashboard)/stories/[id]/page.tsx
+import { StoryHeader } from '@/components/projects/story-header';
+
+// Inside component
+ setIsEditDialogOpen(true)}
+ onDelete={() => setIsDeleteDialogOpen(true)}
+/>
+```
+
+## Acceptance Criteria
+
+- [ ] Header displays Story title (max 2 lines, ellipsis overflow)
+- [ ] Back button navigates to parent Epic detail page
+- [ ] ESC key also navigates back
+- [ ] Status badge shows correct color (Backlog/Todo/InProgress/Done)
+- [ ] Priority badge shows correct color (Low/Medium/High/Critical)
+- [ ] Edit button opens Story form dialog (handler passed from page)
+- [ ] Delete button opens confirmation dialog (handler passed from page)
+- [ ] Responsive layout: Stack vertically on mobile
+- [ ] All buttons have proper ARIA labels
+- [ ] Keyboard accessible (Tab navigation works)
+
+## Testing
+
+**Manual Testing**:
+1. View Story detail page
+2. Verify title displays correctly
+3. Click back button → Navigates to Epic detail
+4. Press ESC key → Navigates to Epic detail
+5. Verify status badge color matches status value
+6. Verify priority badge color matches priority value
+7. Click Edit button → Triggers onEdit callback
+8. Click Delete button → Triggers onDelete callback
+9. Test on mobile → Buttons stack vertically
+
+**Unit Test**:
+```typescript
+import { render, screen, fireEvent } from '@testing-library/react';
+import { StoryHeader } from './story-header';
+
+describe('StoryHeader', () => {
+ const mockStory = {
+ id: '123',
+ title: 'Test Story',
+ status: 'InProgress',
+ priority: 'High',
+ epicId: 'epic-123',
+ };
+
+ it('renders story title', () => {
+ render();
+ expect(screen.getByText('Test Story')).toBeInTheDocument();
+ });
+
+ it('calls onEdit when edit button clicked', () => {
+ const onEdit = jest.fn();
+ render();
+ fireEvent.click(screen.getByLabelText('Edit Story'));
+ expect(onEdit).toHaveBeenCalled();
+ });
+
+ it('calls onDelete when delete button clicked', () => {
+ const onDelete = jest.fn();
+ render();
+ fireEvent.click(screen.getByLabelText('Delete Story'));
+ expect(onDelete).toHaveBeenCalled();
+ });
+});
+```
+
+## Dependencies
+
+**Prerequisites**:
+- Task 1 (page structure must exist)
+- ✅ shadcn/ui Button, Badge components
+
+**Blocks**:
+- Task 5 (Edit/Delete dialogs need header buttons)
+
+## Estimated Time
+
+3 hours
+
+## Notes
+
+Reuse badge color logic from Epic header component if it exists. Keep the component simple and focused - dialogs are handled in Task 5.
diff --git a/docs/plans/sprint_4_story_1_task_3.md b/docs/plans/sprint_4_story_1_task_3.md
new file mode 100644
index 0000000..0a13368
--- /dev/null
+++ b/docs/plans/sprint_4_story_1_task_3.md
@@ -0,0 +1,298 @@
+---
+task_id: sprint_4_story_1_task_3
+story_id: sprint_4_story_1
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 1
+created_date: 2025-11-05
+estimated_hours: 4
+---
+
+# Task 3: Implement Story Metadata Sidebar Component
+
+## Description
+
+Create the metadata sidebar component that displays Story status, priority, assignee, time tracking, dates, and a parent Epic card. This sidebar provides quick access to Story metadata and context.
+
+## What to Do
+
+1. Create `components/projects/story-metadata-sidebar.tsx`
+2. Display Status dropdown (quick status change)
+3. Display Priority dropdown (quick priority change)
+4. Display Assignee with avatar and change button
+5. Display Time Tracking (Estimated vs Actual hours, progress bar)
+6. Display Dates (Created, Updated with relative time)
+7. Display Parent Epic card (title, status, progress, link)
+8. Add responsive design (sidebar moves to top on mobile)
+9. Integrate with Story update hooks
+
+## Files to Create/Modify
+
+- `components/projects/story-metadata-sidebar.tsx` (new, ~150-200 lines)
+- `components/projects/epic-card-compact.tsx` (optional, for parent Epic display)
+- `app/(dashboard)/stories/[id]/page.tsx` (modify, integrate sidebar)
+
+## Implementation Details
+
+```typescript
+// components/projects/story-metadata-sidebar.tsx
+'use client';
+
+import { useState } from 'react';
+import Link from 'next/link';
+import { Calendar, Clock, User } from 'lucide-react';
+import { Card } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { Button } from '@/components/ui/button';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
+import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
+import { Progress } from '@/components/ui/progress';
+import type { Story, Epic } from '@/types/project';
+import { useChangeStoryStatus, useAssignStory } from '@/lib/hooks/use-stories';
+import { formatDistanceToNow } from 'date-fns';
+
+interface StoryMetadataSidebarProps {
+ story: Story;
+ parentEpic?: Epic;
+}
+
+export function StoryMetadataSidebar({ story, parentEpic }: StoryMetadataSidebarProps) {
+ const changeStatus = useChangeStoryStatus();
+ const assignStory = useAssignStory();
+
+ const handleStatusChange = async (newStatus: string) => {
+ await changeStatus.mutateAsync({ storyId: story.id, status: newStatus });
+ };
+
+ const completionPercentage = story.actualHours && story.estimatedHours
+ ? Math.min(100, (story.actualHours / story.estimatedHours) * 100)
+ : 0;
+
+ return (
+
+ {/* Status Section */}
+
+ Status
+
+
+
+ {/* Priority Section */}
+
+ Priority
+
+ {story.priority}
+
+
+
+ {/* Assignee Section */}
+
+ Assignee
+ {story.assigneeId ? (
+
+
+
+
+
+
+
+
+
Assignee Name
+
+
+
+ ) : (
+
+ )}
+
+
+ {/* Time Tracking Section */}
+
+ Time Tracking
+
+
+ Estimated
+ {story.estimatedHours || 0}h
+
+
+ Actual
+ {story.actualHours || 0}h
+
+
+
+ {Math.round(completionPercentage)}% complete
+
+
+
+
+ {/* Dates Section */}
+
+ Dates
+
+
+
+ Created {formatDistanceToNow(new Date(story.createdAt), { addSuffix: true })}
+
+
+
+ Updated {formatDistanceToNow(new Date(story.updatedAt), { addSuffix: true })}
+
+
+
+
+ {/* Parent Epic Section */}
+ {parentEpic && (
+
+ Parent Epic
+
+
+
{parentEpic.title}
+
+ {parentEpic.status}
+
+
+
+ View Epic Details →
+
+
+
+ )}
+
+ );
+}
+
+function getPriorityVariant(priority: string) {
+ const variants = {
+ Low: 'default',
+ Medium: 'warning',
+ High: 'destructive',
+ Critical: 'destructive',
+ };
+ return variants[priority] || 'default';
+}
+```
+
+**Integration in page.tsx**:
+```typescript
+// app/(dashboard)/stories/[id]/page.tsx
+import { StoryMetadataSidebar } from '@/components/projects/story-metadata-sidebar';
+import { useEpic } from '@/lib/hooks/use-epics';
+
+// Inside component
+const { data: story } = useStory(params.id);
+const { data: parentEpic } = useEpic(story?.epicId);
+
+
+```
+
+## Acceptance Criteria
+
+- [ ] Sidebar displays all metadata sections (Status, Priority, Assignee, Time, Dates, Parent Epic)
+- [ ] Status dropdown allows quick status changes
+- [ ] Status changes update immediately (optimistic UI)
+- [ ] Priority badge shows correct color
+- [ ] Assignee section shows avatar and name (if assigned)
+- [ ] Time tracking shows estimated/actual hours and progress bar
+- [ ] Progress bar correctly calculates percentage
+- [ ] Dates display in relative format ("2 hours ago", "3 days ago")
+- [ ] Parent Epic card is clickable and navigates to Epic detail
+- [ ] Parent Epic card shows Epic status and title
+- [ ] Responsive: Sidebar moves above content on mobile
+- [ ] Loading states show skeleton loaders
+
+## Testing
+
+**Manual Testing**:
+1. View Story detail page
+2. Verify all metadata sections display correctly
+3. Change status via dropdown → Verify updates immediately
+4. Verify priority badge color matches priority
+5. Verify assignee displays correctly (if assigned)
+6. Verify time tracking shows correct hours and percentage
+7. Verify dates are in relative format
+8. Click parent Epic card → Navigates to Epic detail page
+9. Test on mobile → Sidebar moves above main content
+
+**Unit Test**:
+```typescript
+import { render, screen } from '@testing-library/react';
+import { StoryMetadataSidebar } from './story-metadata-sidebar';
+
+describe('StoryMetadataSidebar', () => {
+ const mockStory = {
+ id: '123',
+ status: 'InProgress',
+ priority: 'High',
+ assigneeId: 'user-1',
+ estimatedHours: 16,
+ actualHours: 8,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ };
+
+ const mockEpic = {
+ id: 'epic-1',
+ title: 'Parent Epic',
+ status: 'InProgress',
+ };
+
+ it('renders all metadata sections', () => {
+ render();
+ expect(screen.getByText('Status')).toBeInTheDocument();
+ expect(screen.getByText('Priority')).toBeInTheDocument();
+ expect(screen.getByText('Assignee')).toBeInTheDocument();
+ expect(screen.getByText('Time Tracking')).toBeInTheDocument();
+ expect(screen.getByText('Dates')).toBeInTheDocument();
+ expect(screen.getByText('Parent Epic')).toBeInTheDocument();
+ });
+
+ it('calculates time tracking percentage correctly', () => {
+ render();
+ expect(screen.getByText('50% complete')).toBeInTheDocument();
+ });
+});
+```
+
+## Dependencies
+
+**Prerequisites**:
+- Task 1 (page structure must exist)
+- ✅ `useChangeStoryStatus()` hook (already exists)
+- ✅ `useAssignStory()` hook (already exists)
+- ✅ shadcn/ui Card, Badge, Select, Avatar, Progress components
+- ✅ date-fns for relative time formatting
+
+**Blocks**:
+- None (independent component)
+
+## Estimated Time
+
+4 hours
+
+## Notes
+
+**Code Reuse**: Copy sidebar structure from Epic detail page if similar metadata sidebar exists. The parent Epic card is similar to showing a parent Project card in Epic sidebar - reuse that pattern.
+
+**Future Enhancement**: Assignee selector will be enhanced in Story 3 (Enhanced Story Form).
diff --git a/docs/plans/sprint_4_story_1_task_4.md b/docs/plans/sprint_4_story_1_task_4.md
new file mode 100644
index 0000000..f020cbb
--- /dev/null
+++ b/docs/plans/sprint_4_story_1_task_4.md
@@ -0,0 +1,325 @@
+---
+task_id: sprint_4_story_1_task_4
+story_id: sprint_4_story_1
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 1
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 4: Integrate Story API Data Loading and Error Handling
+
+## Description
+
+Connect the Story detail page to the backend API using React Query hooks. Implement data loading, error handling, 404 detection, and loading states with skeleton loaders.
+
+## What to Do
+
+1. Use `useStory(id)` hook to fetch Story data
+2. Use `useEpic(epicId)` hook to fetch parent Epic data
+3. Implement loading states with skeleton loaders
+4. Handle 404 errors (Story not found)
+5. Handle network errors
+6. Handle permission errors (unauthorized)
+7. Add breadcrumb navigation with data
+8. Display Story description in main content area
+9. Test with various Story IDs (valid, invalid, deleted)
+
+## Files to Modify
+
+- `app/(dashboard)/stories/[id]/page.tsx` (modify, ~50 lines added)
+
+## Implementation Details
+
+```typescript
+// app/(dashboard)/stories/[id]/page.tsx
+'use client';
+
+import { use } from 'react';
+import { notFound } from 'next/navigation';
+import { useStory } from '@/lib/hooks/use-stories';
+import { useEpic } from '@/lib/hooks/use-epics';
+import { StoryHeader } from '@/components/projects/story-header';
+import { StoryMetadataSidebar } from '@/components/projects/story-metadata-sidebar';
+import { Card } from '@/components/ui/card';
+import { Skeleton } from '@/components/ui/skeleton';
+import { Alert, AlertDescription } from '@/components/ui/alert';
+import { AlertCircle } from 'lucide-react';
+
+interface StoryDetailPageProps {
+ params: Promise<{ id: string }>;
+}
+
+export default function StoryDetailPage({ params }: StoryDetailPageProps) {
+ const { id } = use(params);
+
+ // Fetch Story data
+ const {
+ data: story,
+ isLoading: storyLoading,
+ error: storyError,
+ } = useStory(id);
+
+ // Fetch parent Epic data
+ const {
+ data: parentEpic,
+ isLoading: epicLoading,
+ } = useEpic(story?.epicId, {
+ enabled: !!story?.epicId, // Only fetch if Story loaded
+ });
+
+ // Handle loading state
+ if (storyLoading || epicLoading) {
+ return ;
+ }
+
+ // Handle 404 - Story not found
+ if (storyError?.response?.status === 404) {
+ notFound();
+ }
+
+ // Handle other errors (network, permission, etc.)
+ if (storyError || !story) {
+ return (
+
+
+
+
+ {storyError?.message || 'Failed to load Story. Please try again.'}
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Breadcrumb Navigation */}
+
+
+ {/* Story Header */}
+
setIsEditDialogOpen(true)}
+ onDelete={() => setIsDeleteDialogOpen(true)}
+ />
+
+ {/* Two-column layout */}
+
+ {/* Main Content Column */}
+
+ {/* Story Description Card */}
+
+ Description
+ {story.description ? (
+
+ ) : (
+ No description provided.
+ )}
+
+
+ {/* Acceptance Criteria Card - Story 3 */}
+
+ Acceptance Criteria
+
+ Acceptance criteria will be added in Story 3.
+
+
+
+ {/* Tasks Section - Story 2 */}
+
+ Tasks
+
+ Task list will be added in Story 2.
+
+
+
+
+ {/* Metadata Sidebar Column */}
+
+
+
+ );
+}
+
+// Loading skeleton
+function StoryDetailPageSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+**404 Page** (optional, create if doesn't exist):
+```typescript
+// app/(dashboard)/stories/[id]/not-found.tsx
+import Link from 'next/link';
+import { Button } from '@/components/ui/button';
+import { Card } from '@/components/ui/card';
+import { FileQuestion } from 'lucide-react';
+
+export default function StoryNotFound() {
+ return (
+
+
+
+ Story Not Found
+
+ The Story you're looking for doesn't exist or you don't have permission to view it.
+
+
+
+
+
+
+
+ );
+}
+```
+
+## Acceptance Criteria
+
+- [ ] Page fetches Story data using `useStory(id)` hook
+- [ ] Page fetches parent Epic data using `useEpic(epicId)` hook
+- [ ] Loading state displays skeleton loaders
+- [ ] 404 error handled (Story not found → shows custom 404 page)
+- [ ] Network errors display error message with retry option
+- [ ] Permission errors display appropriate message
+- [ ] Breadcrumb navigation shows full hierarchy with clickable links
+- [ ] Story description displays correctly (supports line breaks)
+- [ ] Empty description shows placeholder message
+- [ ] All data populates components (header, sidebar)
+- [ ] No console errors during data fetching
+
+## Testing
+
+**Manual Testing**:
+1. Navigate to valid Story ID → Verify data loads correctly
+2. Navigate to invalid Story ID → Verify 404 page shows
+3. Disconnect internet → Navigate to Story → Verify error message
+4. Verify skeleton loaders show during initial load
+5. Verify breadcrumb links are clickable and navigate correctly
+6. Verify Story description displays with line breaks preserved
+7. Test with Story that has no description → Verify placeholder shows
+
+**Test Cases**:
+```bash
+# Valid Story
+/stories/[valid-story-id] → Should load Story details
+
+# Invalid Story
+/stories/invalid-id → Should show 404 page
+
+# Deleted Story
+/stories/[deleted-story-id] → Should show 404 page
+
+# No permission
+/stories/[other-tenant-story-id] → Should show error message
+```
+
+**E2E Test**:
+```typescript
+test('story detail page loads data correctly', async ({ page }) => {
+ await page.goto('/stories/story-123');
+
+ // Wait for data to load
+ await expect(page.locator('h1')).toContainText('Story Title');
+
+ // Verify metadata displays
+ await expect(page.locator('[data-testid="story-status"]')).toBeVisible();
+ await expect(page.locator('[data-testid="story-priority"]')).toBeVisible();
+
+ // Verify description
+ await expect(page.getByText('Description')).toBeVisible();
+});
+
+test('handles 404 for invalid story', async ({ page }) => {
+ await page.goto('/stories/invalid-id');
+ await expect(page.getByText('Story Not Found')).toBeVisible();
+});
+```
+
+## Dependencies
+
+**Prerequisites**:
+- Task 1 (page structure must exist)
+- Task 2 (header component must exist)
+- Task 3 (sidebar component must exist)
+- ✅ `useStory(id)` hook (already exists)
+- ✅ `useEpic(id)` hook (already exists)
+
+**Blocks**:
+- Task 5 (Edit/Delete actions need data loaded)
+
+## Estimated Time
+
+3 hours
+
+## Notes
+
+**Error Handling Strategy**:
+- 404 (Not Found) → Custom 404 page with helpful navigation
+- 403 (Forbidden) → Error message with permission context
+- 500 (Server Error) → Error message with retry button
+- Network Error → Error message with offline indicator
+
+**Performance**: Use React Query's built-in caching. Subsequent visits to the same Story load instantly from cache.
diff --git a/docs/plans/sprint_4_story_1_task_5.md b/docs/plans/sprint_4_story_1_task_5.md
new file mode 100644
index 0000000..e93240c
--- /dev/null
+++ b/docs/plans/sprint_4_story_1_task_5.md
@@ -0,0 +1,309 @@
+---
+task_id: sprint_4_story_1_task_5
+story_id: sprint_4_story_1
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 1
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 5: Add Edit and Delete Actions with Dialogs
+
+## Description
+
+Implement Edit and Delete functionality for Stories in the detail page. Reuse existing Story form dialog for editing and create a confirmation dialog for deletion with cascade warning.
+
+## What to Do
+
+1. Add state management for dialog visibility (Edit dialog, Delete dialog)
+2. Integrate existing Story form component for Edit action
+3. Create Delete confirmation dialog
+4. Show cascade warning if Story has Tasks (list affected Tasks)
+5. Implement delete mutation with error handling
+6. Add success/error toast notifications
+7. Handle post-delete navigation (redirect to Epic detail page)
+8. Test Edit and Delete flows
+
+## Files to Modify
+
+- `app/(dashboard)/stories/[id]/page.tsx` (modify, ~80 lines added for dialogs)
+
+## Implementation Details
+
+```typescript
+// app/(dashboard)/stories/[id]/page.tsx
+'use client';
+
+import { use, useState } from 'react';
+import { useRouter } from 'next/navigation';
+import { useStory, useUpdateStory, useDeleteStory } from '@/lib/hooks/use-stories';
+import { useTasks } from '@/lib/hooks/use-tasks';
+import { StoryForm } from '@/components/projects/story-form';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from '@/components/ui/alert-dialog';
+import { Button } from '@/components/ui/button';
+import { toast } from 'sonner';
+import { Loader2, AlertTriangle } from 'lucide-react';
+
+export default function StoryDetailPage({ params }: StoryDetailPageProps) {
+ const { id } = use(params);
+ const router = useRouter();
+
+ // Fetch data
+ const { data: story } = useStory(id);
+ const { data: tasks = [] } = useTasks(id);
+
+ // Dialog state
+ const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
+ const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
+
+ // Mutations
+ const updateStory = useUpdateStory();
+ const deleteStory = useDeleteStory();
+
+ // Edit Story handler
+ const handleEditStory = async (data: UpdateStoryDto) => {
+ try {
+ await updateStory.mutateAsync({ id: story.id, data });
+ setIsEditDialogOpen(false);
+ toast.success('Story updated successfully');
+ } catch (error) {
+ toast.error('Failed to update Story');
+ console.error(error);
+ }
+ };
+
+ // Delete Story handler
+ const handleDeleteStory = async () => {
+ try {
+ await deleteStory.mutateAsync(story.id);
+ toast.success('Story deleted successfully');
+ // Redirect to parent Epic detail page
+ router.push(`/epics/${story.epicId}`);
+ } catch (error) {
+ toast.error('Failed to delete Story');
+ console.error(error);
+ setIsDeleteDialogOpen(false);
+ }
+ };
+
+ return (
+
+ {/* ... existing content ... */}
+
+
setIsEditDialogOpen(true)}
+ onDelete={() => setIsDeleteDialogOpen(true)}
+ />
+
+ {/* ... rest of content ... */}
+
+ {/* Edit Story Dialog */}
+
+
+ {/* Delete Story Confirmation Dialog */}
+
+
+
+
+
+ Delete Story?
+
+
+ This will permanently delete {story?.title} and all associated data.
+ This action cannot be undone.
+
+ {/* Cascade warning if Tasks exist */}
+ {tasks.length > 0 && (
+
+
+ ⚠️ This Story has {tasks.length} Task{tasks.length > 1 ? 's' : ''} that will also be deleted:
+
+
+ {tasks.slice(0, 5).map((task) => (
+ - {task.title}
+ ))}
+ {tasks.length > 5 && (
+ - ... and {tasks.length - 5} more
+ )}
+
+
+ )}
+
+
+
+
+
+ Cancel
+
+
+ {deleteStory.isPending && }
+ Delete Story
+
+
+
+
+
+ );
+}
+```
+
+## Acceptance Criteria
+
+- [ ] Edit button opens Story form dialog with current Story data
+- [ ] Story form displays in "edit" mode with pre-filled values
+- [ ] Editing Story updates data correctly
+- [ ] Edit success shows toast notification
+- [ ] Edit error shows error toast
+- [ ] Dialog closes after successful edit
+- [ ] Delete button opens confirmation dialog
+- [ ] Confirmation dialog shows Story title
+- [ ] If Story has Tasks, shows cascade warning with Task list
+- [ ] Delete action removes Story and navigates to Epic detail page
+- [ ] Delete success shows toast notification
+- [ ] Delete error shows error toast and keeps user on page
+- [ ] Dialog buttons disabled during mutation (loading state)
+- [ ] ESC key closes dialogs
+- [ ] Outside click closes dialogs (configurable)
+
+## Testing
+
+**Manual Testing**:
+
+**Edit Story Flow**:
+1. Click Edit button → Verify dialog opens
+2. Verify form pre-filled with current Story data
+3. Change title → Save → Verify title updates on page
+4. Change description → Save → Verify description updates
+5. Change priority → Save → Verify priority badge updates
+6. Test validation → Submit empty title → Verify error message
+7. Click Cancel → Verify dialog closes without saving
+8. Press ESC → Verify dialog closes
+
+**Delete Story Flow**:
+1. Click Delete button → Verify confirmation dialog opens
+2. Verify Story title displayed in confirmation message
+3. If Story has Tasks → Verify cascade warning shows with Task list
+4. Click Cancel → Verify dialog closes, Story not deleted
+5. Click Delete Story → Verify Story deleted
+6. Verify redirected to Epic detail page
+7. Verify success toast shown
+8. Go to Epic page → Verify Story no longer in list
+
+**Error Handling**:
+1. Disconnect internet
+2. Try to edit Story → Verify error toast shown
+3. Try to delete Story → Verify error toast shown
+4. Verify user stays on page after error
+
+**E2E Test**:
+```typescript
+test('user can edit story', async ({ page }) => {
+ await page.goto('/stories/story-123');
+
+ // Open edit dialog
+ await page.click('[aria-label="Edit Story"]');
+ await expect(page.getByRole('dialog')).toBeVisible();
+
+ // Edit title
+ const titleInput = page.locator('[name="title"]');
+ await titleInput.fill('Updated Story Title');
+
+ // Save
+ await page.click('button:has-text("Save")');
+
+ // Verify updated
+ await expect(page.locator('h1')).toContainText('Updated Story Title');
+ await expect(page.getByText('Story updated successfully')).toBeVisible();
+});
+
+test('user can delete story', async ({ page }) => {
+ await page.goto('/stories/story-123');
+
+ // Open delete dialog
+ await page.click('[aria-label="Delete Story"]');
+ await expect(page.getByRole('alertdialog')).toBeVisible();
+
+ // Confirm delete
+ await page.click('button:has-text("Delete Story")');
+
+ // Verify redirected
+ await expect(page).toHaveURL(/\/epics\//);
+ await expect(page.getByText('Story deleted successfully')).toBeVisible();
+});
+```
+
+## Dependencies
+
+**Prerequisites**:
+- Task 1, 2, 3, 4 (page, header, sidebar, data loading must exist)
+- ✅ StoryForm component (already exists)
+- ✅ `useUpdateStory()` hook (already exists)
+- ✅ `useDeleteStory()` hook (already exists)
+- ✅ `useTasks(storyId)` hook (for cascade warning)
+- ✅ shadcn/ui Dialog, AlertDialog components
+- ✅ sonner for toast notifications
+
+**Blocks**:
+- None (final task for Story 1)
+
+## Estimated Time
+
+3 hours
+
+## Notes
+
+**Cascade Warning**: Show list of Tasks that will be deleted when Story is deleted. This helps users understand the impact of deletion and prevents accidental data loss.
+
+**Navigation After Delete**: Always redirect to the parent Epic detail page after successful deletion. This prevents users from staying on a deleted Story page.
+
+**Form Reuse**: The StoryForm component already exists and handles both "create" and "edit" modes. No changes needed to the form itself.
+
+**Error Handling**: Use optimistic UI for updates - update immediately, revert on error. For deletes, wait for confirmation before navigating.
diff --git a/docs/plans/sprint_4_story_1_task_6.md b/docs/plans/sprint_4_story_1_task_6.md
new file mode 100644
index 0000000..bf75709
--- /dev/null
+++ b/docs/plans/sprint_4_story_1_task_6.md
@@ -0,0 +1,403 @@
+---
+task_id: sprint_4_story_1_task_6
+story_id: sprint_4_story_1
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 1
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 6: Implement Responsive Design and Accessibility
+
+## Description
+
+Ensure the Story detail page works flawlessly on all screen sizes (mobile, tablet, desktop) and meets WCAG 2.1 Level AA accessibility standards. This includes responsive layout adjustments, keyboard navigation, ARIA labels, and focus management.
+
+## What to Do
+
+1. Test and fix responsive layout on all breakpoints
+2. Implement mobile-specific UI adjustments (sidebar to tabs)
+3. Add keyboard navigation support (Tab, Enter, ESC)
+4. Add ARIA labels to all interactive elements
+5. Implement focus management (dialog open/close)
+6. Ensure proper heading hierarchy (h1 → h2 → h3)
+7. Test color contrast ratios (WCAG AA: 4.5:1 minimum)
+8. Test with screen reader (NVDA/JAWS)
+9. Add skip links for keyboard users
+10. Run Lighthouse accessibility audit
+
+## Files to Modify
+
+- `app/(dashboard)/stories/[id]/page.tsx` (modify, responsive tweaks)
+- `components/projects/story-header.tsx` (modify, accessibility)
+- `components/projects/story-metadata-sidebar.tsx` (modify, responsive)
+- All related components (add ARIA labels)
+
+## Implementation Details
+
+### Responsive Layout Breakpoints
+
+```typescript
+// app/(dashboard)/stories/[id]/page.tsx
+// Adjust grid layout for different screens
+
+
+ {/* Main content - full width on mobile, 70% on desktop */}
+
+ {/* Story description, acceptance criteria, tasks */}
+
+
+ {/* Sidebar - full width on mobile (at top), fixed width on desktop */}
+
+
+```
+
+### Mobile-Specific Adjustments
+
+```typescript
+// Convert sidebar sections to tabs on mobile
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+
+function MobileStoryMetadata({ story, parentEpic }: Props) {
+ return (
+
+
+ Details
+ Time
+ Epic
+
+
+
+ {/* Status, Priority, Assignee */}
+
+
+
+ {/* Time tracking, Dates */}
+
+
+
+ {/* Parent Epic card */}
+
+
+ );
+}
+```
+
+### ARIA Labels and Roles
+
+```typescript
+// Story header with accessibility
+
+
+ {story.title}
+
+
+
+
+ {story.status}
+
+
+
+ {story.priority}
+
+
+
+
+
+
+ Opens a dialog to edit story title, description, and metadata
+
+
+
+
+ Permanently deletes this story and all associated tasks
+
+
+
+
+// Main content sections
+
+
+ Description
+
+ {story.description}
+
+
+
+
+ Acceptance Criteria
+ {/* Criteria list */}
+
+
+
+ Tasks
+ {/* Tasks list */}
+
+
+
+// Sidebar
+
+```
+
+### Keyboard Navigation
+
+```typescript
+// Add keyboard shortcuts
+useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ // ESC - Go back to Epic
+ if (e.key === 'Escape' && !isEditDialogOpen && !isDeleteDialogOpen) {
+ router.push(`/epics/${story.epicId}`);
+ }
+
+ // Cmd/Ctrl + E - Edit Story
+ if ((e.metaKey || e.ctrlKey) && e.key === 'e') {
+ e.preventDefault();
+ setIsEditDialogOpen(true);
+ }
+
+ // Cmd/Ctrl + Backspace - Delete Story
+ if ((e.metaKey || e.ctrlKey) && e.key === 'Backspace') {
+ e.preventDefault();
+ setIsDeleteDialogOpen(true);
+ }
+ };
+
+ window.addEventListener('keydown', handleKeyDown);
+ return () => window.removeEventListener('keydown', handleKeyDown);
+}, [isEditDialogOpen, isDeleteDialogOpen]);
+
+// Add keyboard shortcut hints
+
+```
+
+### Focus Management
+
+```typescript
+// Trap focus in dialogs
+import { useFocusTrap } from '@/lib/hooks/use-focus-trap';
+
+function EditStoryDialog({ isOpen, onClose }: Props) {
+ const dialogRef = useRef(null);
+ useFocusTrap(dialogRef, isOpen);
+
+ // Auto-focus first field when dialog opens
+ useEffect(() => {
+ if (isOpen) {
+ const firstInput = dialogRef.current?.querySelector('input');
+ firstInput?.focus();
+ }
+ }, [isOpen]);
+
+ // Return focus to trigger element when dialog closes
+ const triggerRef = useRef(null);
+
+ const handleOpen = () => {
+ triggerRef.current = document.activeElement as HTMLButtonElement;
+ setIsOpen(true);
+ };
+
+ const handleClose = () => {
+ setIsOpen(false);
+ triggerRef.current?.focus();
+ };
+
+ return (
+
+ );
+}
+```
+
+### Skip Links
+
+```typescript
+// Add skip link for keyboard users
+
+ Skip to main content
+
+
+
+ {/* Main content */}
+
+```
+
+## Acceptance Criteria
+
+### Responsive Design
+- [ ] Desktop (> 1024px): Two-column layout (content + sidebar)
+- [ ] Tablet (640px - 1024px): Two-column layout with narrower sidebar
+- [ ] Mobile (< 640px): Single-column layout, sidebar moves to top or tabs
+- [ ] All buttons and controls accessible on touch devices
+- [ ] No horizontal scrolling on any screen size
+- [ ] Text remains readable on all screen sizes (minimum 14px)
+
+### Accessibility (WCAG 2.1 Level AA)
+- [ ] All interactive elements keyboard accessible (Tab navigation)
+- [ ] Focus indicators visible (2px outline, high contrast)
+- [ ] ARIA labels on all buttons, links, and form controls
+- [ ] Proper heading hierarchy (h1 → h2 → h3)
+- [ ] Color contrast ratio >= 4.5:1 for all text
+- [ ] Large text (18px+) contrast ratio >= 3:1
+- [ ] Screen reader announces all content correctly
+- [ ] Focus trapped in dialogs (cannot Tab outside)
+- [ ] Focus returns to trigger element when dialog closes
+- [ ] Skip links available for keyboard users
+- [ ] Icons have `aria-hidden="true"` (text labels present)
+- [ ] Loading states announced to screen readers (`role="status"`)
+- [ ] Error messages announced (`role="alert"`)
+
+### Keyboard Navigation
+- [ ] Tab - Navigate through interactive elements
+- [ ] Enter - Activate buttons and links
+- [ ] Space - Toggle checkboxes and buttons
+- [ ] ESC - Close dialogs and navigate back
+- [ ] Cmd/Ctrl + E - Edit Story (custom shortcut)
+- [ ] Cmd/Ctrl + Backspace - Delete Story (custom shortcut)
+
+## Testing
+
+### Manual Testing
+
+**Responsive Design**:
+1. Open Story detail page on desktop → Verify two-column layout
+2. Resize to tablet width (768px) → Verify layout adjusts
+3. Resize to mobile width (375px) → Verify single-column layout
+4. Verify sidebar moves to top on mobile
+5. Test all buttons clickable on touch device
+6. Verify no horizontal scrolling on any screen
+7. Test landscape and portrait orientations
+
+**Keyboard Navigation**:
+1. Navigate to Story page using only keyboard
+2. Press Tab → Verify focus moves through all interactive elements
+3. Verify focus indicator visible (blue outline)
+4. Press ESC → Verify navigates back to Epic
+5. Press Cmd/Ctrl + E → Verify Edit dialog opens
+6. Press Tab in dialog → Verify focus trapped
+7. Press ESC in dialog → Verify dialog closes and focus returns
+
+**Screen Reader** (NVDA/JAWS):
+1. Enable screen reader
+2. Navigate to Story page
+3. Verify page structure announced (heading hierarchy)
+4. Verify all buttons have labels
+5. Verify badges announced with context ("Status: In Progress")
+6. Verify loading states announced
+7. Verify error messages announced
+8. Open Edit dialog → Verify form fields labeled correctly
+
+### Automated Testing
+
+**Lighthouse Audit**:
+```bash
+# Run Lighthouse accessibility audit
+npm run lighthouse
+
+# Expected results:
+# Accessibility: >= 90 (ideally 100)
+# Best Practices: >= 90
+# SEO: >= 90
+# Performance: >= 80 (acceptable for dynamic content)
+```
+
+**axe DevTools**:
+```bash
+# Install axe DevTools browser extension
+# Open Story page
+# Run axe scan
+# Expected: 0 violations
+```
+
+### Color Contrast Testing
+
+```bash
+# Test all text colors
+# Use browser DevTools > Accessibility > Color contrast
+
+# Required ratios (WCAG AA):
+# Normal text (< 18px): 4.5:1
+# Large text (>= 18px): 3:1
+# UI components: 3:1
+
+# Test combinations:
+Story Title (32px): Should pass (large text)
+Description (16px): Should pass 4.5:1
+Metadata labels (14px): Should pass 4.5:1
+Badges: Should pass 3:1 (UI components)
+Buttons: Should pass 3:1 (UI components)
+```
+
+## Dependencies
+
+**Prerequisites**:
+- Task 1, 2, 3, 4, 5 (all components must exist)
+- ✅ shadcn/ui components with built-in accessibility
+- ✅ Tailwind CSS responsive utilities
+- ✅ ARIA attributes support
+
+**Blocks**:
+- None (final polish task)
+
+## Estimated Time
+
+3 hours
+
+## Notes
+
+**Testing Tools**:
+- Chrome DevTools > Lighthouse (accessibility audit)
+- axe DevTools browser extension (automated WCAG testing)
+- NVDA/JAWS screen reader (manual testing)
+- WAVE browser extension (visual accessibility check)
+- Contrast Checker (color contrast ratios)
+
+**Common Accessibility Issues to Avoid**:
+- Missing alt text on images
+- Insufficient color contrast
+- Missing ARIA labels on icon-only buttons
+- Broken keyboard navigation (focus trap)
+- Missing heading hierarchy (h1 → h3, skip h2)
+- Form inputs without labels
+- Loading states not announced to screen readers
+
+**Responsive Design Checklist**:
+- Test on real devices (iPhone, Android, tablet)
+- Test different orientations (portrait, landscape)
+- Test different zoom levels (100%, 150%, 200%)
+- Verify touch targets >= 44x44px
+- No horizontal scrolling on any screen
diff --git a/docs/plans/sprint_4_story_2.md b/docs/plans/sprint_4_story_2.md
new file mode 100644
index 0000000..40eb293
--- /dev/null
+++ b/docs/plans/sprint_4_story_2.md
@@ -0,0 +1,278 @@
+---
+story_id: sprint_4_story_2
+sprint: sprint_4
+priority: P0
+status: not_started
+story_points: 5
+estimated_days: 2
+created_date: 2025-11-05
+assignee: Frontend Team
+---
+
+# Story 2: Task Management in Story Detail
+
+**Sprint**: Sprint 4
+**Priority**: P0 (Critical)
+**Estimated**: 2 days
+**Owner**: Frontend Team
+
+## Description
+
+Implement comprehensive Task management within the Story detail page, including Task list display, inline Task creation, Task status updates via checkbox, Task filtering, and Task sorting. This enables users to break down Stories into granular implementation tasks and track their progress.
+
+## User Story
+
+**As a** developer or project manager,
+**I want** to create and manage Tasks within a Story,
+**So that** I can break down Stories into smaller work items and track implementation progress.
+
+## Acceptance Criteria
+
+- [ ] Task list displays all Tasks associated with the Story
+- [ ] Empty state shows when Story has no Tasks
+- [ ] "Add Task" button opens inline Task creation form
+- [ ] Inline form allows creating Tasks without leaving the page
+- [ ] Task checkbox toggles Task status (Todo ↔ Done)
+- [ ] Task cards display title, priority, assignee, estimated hours
+- [ ] Task count badge shows in Story header (e.g., "Tasks (8)")
+- [ ] Task filters allow filtering by status, priority, assignee
+- [ ] Task sorting allows sorting by priority, status, created date, assignee
+- [ ] Task form validates inputs (title required, hours numeric)
+- [ ] Success/error toast notifications for Task operations
+- [ ] Loading states during Task creation/update
+- [ ] Optimistic UI updates for instant feedback
+
+## Technical Requirements
+
+**New Components**:
+- `components/projects/task-list.tsx` - Task list container (300-400 lines)
+- `components/projects/task-card.tsx` - Individual Task card (150-200 lines)
+- `components/projects/task-form.tsx` - Inline Task creation form (200-250 lines)
+
+**New Hooks**:
+- `lib/hooks/use-tasks.ts` - Task CRUD hooks (150-200 lines)
+ - `useTasks(storyId)` - List Tasks by Story
+ - `useCreateTask()` - Create Task
+ - `useUpdateTask()` - Update Task
+ - `useDeleteTask()` - Delete Task
+ - `useChangeTaskStatus()` - Change Task status (checkbox)
+
+**API Integration**:
+- `GET /api/v1/stories/{storyId}/tasks` - List Tasks
+- `POST /api/v1/tasks` - Create Task
+- `PUT /api/v1/tasks/{id}` - Update Task
+- `DELETE /api/v1/tasks/{id}` - Delete Task
+- `PUT /api/v1/tasks/{id}/status` - Change status
+
+**Task Types** (add to `types/project.ts`):
+```typescript
+export interface Task {
+ id: string;
+ title: string;
+ description?: string;
+ storyId: string;
+ projectId: string;
+ status: WorkItemStatus;
+ priority: WorkItemPriority;
+ estimatedHours?: number;
+ actualHours?: number;
+ assigneeId?: string;
+ tenantId: string;
+ createdAt: string;
+ updatedAt: string;
+}
+
+export interface CreateTaskDto {
+ storyId: string;
+ title: string;
+ description?: string;
+ priority: WorkItemPriority;
+ estimatedHours?: number;
+}
+
+export interface UpdateTaskDto {
+ title?: string;
+ description?: string;
+ priority?: WorkItemPriority;
+ estimatedHours?: number;
+ actualHours?: number;
+}
+```
+
+## Tasks
+
+- [ ] [Task 1](sprint_4_story_2_task_1.md) - Verify Task API endpoints and create Task types
+- [ ] [Task 2](sprint_4_story_2_task_2.md) - Create Task API client and React Query hooks
+- [ ] [Task 3](sprint_4_story_2_task_3.md) - Implement TaskList component with filters and sorting
+- [ ] [Task 4](sprint_4_story_2_task_4.md) - Implement TaskCard component with checkbox status toggle
+- [ ] [Task 5](sprint_4_story_2_task_5.md) - Implement inline TaskForm for Task creation
+- [ ] [Task 6](sprint_4_story_2_task_6.md) - Integrate Task management into Story detail page
+
+**Progress**: 0/6 tasks completed
+
+## Dependencies
+
+**Prerequisites**:
+- ✅ Task API endpoints ready (TasksController.cs)
+- Story 1 completed (Story detail page must exist)
+
+**Blocks**:
+- None (independent feature after Story 1)
+
+## Definition of Done
+
+- All 6 tasks completed
+- Task list displays Tasks correctly
+- Task creation form working (inline)
+- Task checkbox status toggle working
+- Task filters and sorting functional
+- Task count badge updates in real-time
+- Empty state displays when no Tasks
+- Loading and error states implemented
+- Optimistic UI updates for status changes
+- Code reviewed and approved
+- E2E tests passing
+- Git commit created with descriptive message
+
+## Test Plan
+
+**Manual Testing**:
+1. Navigate to Story detail page
+2. Verify "Add Task" button visible
+3. Click "Add Task" → Verify inline form appears
+4. Create Task with title "Implement login API" → Verify Task appears in list
+5. Create Task without title → Verify validation error
+6. Click Task checkbox → Verify status changes to Done
+7. Uncheck Task checkbox → Verify status changes back to Todo
+8. Verify Task count badge updates (e.g., "Tasks (3)")
+9. Filter by status: "Done" → Verify only Done Tasks show
+10. Sort by priority → Verify Tasks reorder correctly
+11. Create 10+ Tasks → Verify list scrolls properly
+12. Test on mobile → Verify responsive layout
+
+**E2E Test** (Playwright):
+```typescript
+test('user can create and manage tasks', async ({ page }) => {
+ await page.goto('/stories/story-123');
+
+ // Create Task
+ await page.click('[data-testid="add-task-button"]');
+ await page.fill('[name="title"]', 'Implement login form');
+ await page.selectOption('[name="priority"]', 'High');
+ await page.fill('[name="estimatedHours"]', '8');
+ await page.click('[data-testid="submit-task"]');
+
+ // Verify Task appears
+ await expect(page.locator('[data-testid="task-item"]')).toContainText('Implement login form');
+
+ // Toggle status
+ await page.click('[data-testid="task-checkbox"]');
+ await expect(page.locator('[data-testid="task-status"]')).toContainText('Done');
+
+ // Verify count updates
+ await expect(page.locator('[data-testid="task-count"]')).toContainText('1');
+});
+
+test('task filters work correctly', async ({ page }) => {
+ await page.goto('/stories/story-123');
+
+ // Create Done Task
+ await page.click('[data-testid="add-task-button"]');
+ await page.fill('[name="title"]', 'Task 1');
+ await page.click('[data-testid="submit-task"]');
+ await page.click('[data-testid="task-checkbox"]');
+
+ // Create Todo Task
+ await page.click('[data-testid="add-task-button"]');
+ await page.fill('[name="title"]', 'Task 2');
+ await page.click('[data-testid="submit-task"]');
+
+ // Filter by Done
+ await page.selectOption('[data-testid="task-filter"]', 'Done');
+ await expect(page.locator('[data-testid="task-item"]')).toHaveCount(1);
+ await expect(page.locator('[data-testid="task-item"]')).toContainText('Task 1');
+});
+```
+
+**Verification Commands**:
+```bash
+# Start dev server
+npm run dev
+
+# Navigate to Story detail page
+# Create Tasks and test functionality
+
+# Run E2E tests
+npm run test:e2e -- tasks
+```
+
+## UX Design Reference
+
+**Design Document**: `docs/designs/STORY_UX_UI_DESIGN.md`
+- Section: "Tasks Section" (lines 305-348)
+- Layout: Task cards with checkbox, metadata, actions
+- Interaction: Checkbox toggles status, click card to expand details
+- Filters: All, Todo, InProgress, Done
+- Sorting: Priority, Status, Created date, Assignee
+
+**Design Tokens**:
+```css
+/* Task Card */
+.task-card {
+ padding: 12px 16px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+}
+
+.task-card:hover {
+ border-color: var(--primary);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+/* Task Checkbox */
+.task-checkbox {
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+}
+
+.task-checkbox:hover {
+ transform: scale(1.1);
+}
+
+/* Task Count Badge */
+.task-count-badge {
+ background: var(--muted);
+ color: var(--muted-foreground);
+ padding: 2px 8px;
+ border-radius: 12px;
+ font-size: 12px;
+}
+```
+
+## Notes
+
+**Why This Matters**:
+- **Core Feature**: Tasks are essential for breaking down Stories into implementation steps
+- **User Experience**: Inline Task creation enables fast workflow without navigation
+- **Real-time Feedback**: Optimistic UI updates make the app feel instant
+- **Epic → Story → Task**: Completes the full project management hierarchy
+
+**Performance Considerations**:
+- Use React.memo for TaskCard to prevent unnecessary re-renders
+- Debounce filter/sort operations (300ms delay)
+- Virtual scrolling for lists > 50 Tasks (Phase 3)
+- Optimistic updates for status changes (instant UI feedback)
+
+**Future Enhancements** (Post-Sprint 4):
+- Task drag-and-drop reordering
+- Task bulk operations (multi-select)
+- Task due dates and reminders
+- Task comments and activity log
+- Task templates for common patterns
+- Task time tracking (start/stop timer)
+
+---
+
+**Created**: 2025-11-05 by Frontend Agent
+**Updated**: 2025-11-05
diff --git a/docs/plans/sprint_4_story_2_task_1.md b/docs/plans/sprint_4_story_2_task_1.md
new file mode 100644
index 0000000..fe9b8ac
--- /dev/null
+++ b/docs/plans/sprint_4_story_2_task_1.md
@@ -0,0 +1,234 @@
+---
+task_id: sprint_4_story_2_task_1
+story_id: sprint_4_story_2
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 2
+created_date: 2025-11-05
+estimated_hours: 2
+---
+
+# Task 1: Verify Task API Endpoints and Create Task Types
+
+## Description
+
+Verify that all Task API endpoints are working correctly and create TypeScript types for Task entities. This task ensures the backend is ready and establishes the type-safe foundation for Task management.
+
+## What to Do
+
+1. Test Task API endpoints using Postman or Swagger
+2. Verify GET /api/v1/stories/{storyId}/tasks returns Task list
+3. Verify POST /api/v1/tasks creates Task correctly
+4. Verify PUT /api/v1/tasks/{id} updates Task
+5. Verify DELETE /api/v1/tasks/{id} deletes Task
+6. Verify PUT /api/v1/tasks/{id}/status changes status
+7. Check multi-tenant isolation (cannot access other tenant Tasks)
+8. Add Task types to `types/project.ts`
+9. Document any API quirks or issues
+
+## Files to Create/Modify
+
+- `types/project.ts` (modify, add Task types ~50 lines)
+
+## Implementation Details
+
+```typescript
+// types/project.ts
+// Add these Task types
+
+export interface Task {
+ id: string;
+ title: string;
+ description?: string;
+ storyId: string;
+ projectId: string;
+ status: WorkItemStatus;
+ priority: WorkItemPriority;
+ estimatedHours?: number;
+ actualHours?: number;
+ assigneeId?: string;
+ tenantId: string;
+ createdAt: string;
+ updatedAt: string;
+ createdBy?: string;
+ updatedBy?: string;
+}
+
+export interface CreateTaskDto {
+ storyId: string;
+ projectId?: string; // May be auto-filled from Story
+ title: string;
+ description?: string;
+ priority: WorkItemPriority;
+ estimatedHours?: number;
+ createdBy: string;
+}
+
+export interface UpdateTaskDto {
+ title?: string;
+ description?: string;
+ priority?: WorkItemPriority;
+ estimatedHours?: number;
+ actualHours?: number;
+}
+
+export interface ChangeTaskStatusDto {
+ status: WorkItemStatus;
+}
+
+export interface AssignTaskDto {
+ assigneeId: string;
+}
+
+// Add Task to existing types if needed
+export interface Story {
+ // ... existing fields
+ taskCount?: number; // Optional: count of Tasks
+ tasks?: Task[]; // Optional: nested Tasks
+}
+```
+
+## API Testing Checklist
+
+**GET /api/v1/stories/{storyId}/tasks** - List Tasks:
+```bash
+# Expected: 200 OK, array of Tasks
+curl -H "Authorization: Bearer {token}" \
+ GET https://api.colaflow.com/api/v1/stories/{storyId}/tasks
+
+# Test cases:
+# - Valid storyId → Returns tasks array
+# - Empty story → Returns []
+# - Invalid storyId → Returns 404
+# - Other tenant story → Returns 403 or empty array
+```
+
+**POST /api/v1/tasks** - Create Task:
+```bash
+# Expected: 201 Created, Task object
+curl -H "Authorization: Bearer {token}" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "storyId": "story-123",
+ "title": "Implement login API",
+ "description": "Create POST /auth/login endpoint",
+ "priority": "High",
+ "estimatedHours": 8,
+ "createdBy": "user-123"
+ }' \
+ POST https://api.colaflow.com/api/v1/tasks
+
+# Test cases:
+# - Valid data → Creates task
+# - Missing title → Returns 400 validation error
+# - Invalid storyId → Returns 404 or 400
+# - Missing storyId → Returns 400
+```
+
+**PUT /api/v1/tasks/{id}** - Update Task:
+```bash
+# Expected: 200 OK, updated Task object
+curl -H "Authorization: Bearer {token}" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "title": "Updated title",
+ "priority": "Critical"
+ }' \
+ PUT https://api.colaflow.com/api/v1/tasks/{id}
+
+# Test cases:
+# - Valid update → Returns updated task
+# - Invalid taskId → Returns 404
+# - Other tenant task → Returns 403
+```
+
+**DELETE /api/v1/tasks/{id}** - Delete Task:
+```bash
+# Expected: 204 No Content or 200 OK
+curl -H "Authorization: Bearer {token}" \
+ DELETE https://api.colaflow.com/api/v1/tasks/{id}
+
+# Test cases:
+# - Valid taskId → Deletes task
+# - Invalid taskId → Returns 404
+# - Other tenant task → Returns 403
+```
+
+**PUT /api/v1/tasks/{id}/status** - Change Status:
+```bash
+# Expected: 200 OK, updated Task object
+curl -H "Authorization: Bearer {token}" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "status": "Done"
+ }' \
+ PUT https://api.colaflow.com/api/v1/tasks/{id}/status
+
+# Test cases:
+# - Valid status → Updates status
+# - Invalid status → Returns 400
+# - Other tenant task → Returns 403
+```
+
+## Acceptance Criteria
+
+- [ ] All Task API endpoints tested and working
+- [ ] GET endpoint returns Task list for Story
+- [ ] POST endpoint creates Task successfully
+- [ ] PUT endpoint updates Task correctly
+- [ ] DELETE endpoint removes Task
+- [ ] Status change endpoint works
+- [ ] Multi-tenant isolation verified (cannot access other tenant Tasks)
+- [ ] Task types added to `types/project.ts`
+- [ ] All Task fields properly typed
+- [ ] CreateTaskDto, UpdateTaskDto, ChangeTaskStatusDto defined
+- [ ] API quirks documented (if any)
+
+## Testing
+
+**Postman/Swagger Testing**:
+1. Import Task API collection (if available)
+2. Test each endpoint with valid data
+3. Test error cases (invalid IDs, missing fields, validation)
+4. Verify responses match TypeScript types
+5. Document any discrepancies
+
+**Expected API Behavior**:
+- All endpoints require authentication (JWT token)
+- All endpoints respect multi-tenant isolation (TenantId filter)
+- Validation errors return 400 with error details
+- Not found errors return 404
+- Forbidden errors return 403
+- Successful creates return 201 Created
+- Successful updates/deletes return 200 OK or 204 No Content
+
+## Dependencies
+
+**Prerequisites**:
+- ✅ Task API ready (TasksController.cs)
+- ✅ JWT authentication working
+- ✅ Postman or Swagger access
+
+**Blocks**:
+- Task 2 (API client depends on verified endpoints)
+
+## Estimated Time
+
+2 hours
+
+## Notes
+
+**Document API Issues**:
+If you find any API issues, document them clearly:
+- Missing fields in response
+- Unexpected validation rules
+- Incorrect HTTP status codes
+- Multi-tenant isolation not working
+- Performance issues (slow responses)
+
+**Communicate with Backend**:
+If API endpoints are not ready or have issues, immediately notify Backend team and Product Manager. This is a blocker for Story 2.
+
+**Fallback Plan**:
+If Task API is not ready, frontend can proceed with mock data for development, but API must be ready before Story 2 completion.
diff --git a/docs/plans/sprint_4_story_2_task_2.md b/docs/plans/sprint_4_story_2_task_2.md
new file mode 100644
index 0000000..3b0f7e1
--- /dev/null
+++ b/docs/plans/sprint_4_story_2_task_2.md
@@ -0,0 +1,449 @@
+---
+task_id: sprint_4_story_2_task_2
+story_id: sprint_4_story_2
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 2
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 2: Create Task API Client and React Query Hooks
+
+## Description
+
+Create the Task API client module and React Query hooks for Task CRUD operations. This establishes the data layer for Task management with optimistic updates, caching, and error handling.
+
+## What to Do
+
+1. Add Task API client methods to `lib/api/pm.ts`
+2. Create `lib/hooks/use-tasks.ts` with React Query hooks
+3. Implement optimistic updates for instant UI feedback
+4. Add cache invalidation strategies
+5. Add error handling with toast notifications
+6. Add logger integration for debugging
+7. Test all hooks with real API data
+8. Document hook usage and examples
+
+## Files to Create/Modify
+
+- `lib/api/pm.ts` (modify, add Task methods ~100 lines)
+- `lib/hooks/use-tasks.ts` (new, ~150-200 lines)
+
+## Implementation Details
+
+### Task API Client (`lib/api/pm.ts`)
+
+```typescript
+// lib/api/pm.ts
+// Add these Task API methods
+
+import { apiClient } from './client';
+import type {
+ Task,
+ CreateTaskDto,
+ UpdateTaskDto,
+ ChangeTaskStatusDto,
+ AssignTaskDto,
+} from '@/types/project';
+
+export const tasksApi = {
+ /**
+ * Get all Tasks for a Story
+ */
+ list: async (storyId: string): Promise => {
+ const response = await apiClient.get(`/api/v1/stories/${storyId}/tasks`);
+ return response.data;
+ },
+
+ /**
+ * Get single Task by ID
+ */
+ get: async (id: string): Promise => {
+ const response = await apiClient.get(`/api/v1/tasks/${id}`);
+ return response.data;
+ },
+
+ /**
+ * Create new Task
+ */
+ create: async (data: CreateTaskDto): Promise => {
+ const response = await apiClient.post('/api/v1/tasks', data);
+ return response.data;
+ },
+
+ /**
+ * Update Task
+ */
+ update: async (id: string, data: UpdateTaskDto): Promise => {
+ const response = await apiClient.put(`/api/v1/tasks/${id}`, data);
+ return response.data;
+ },
+
+ /**
+ * Delete Task
+ */
+ delete: async (id: string): Promise => {
+ await apiClient.delete(`/api/v1/tasks/${id}`);
+ },
+
+ /**
+ * Change Task status
+ */
+ changeStatus: async (id: string, status: string): Promise => {
+ const response = await apiClient.put(`/api/v1/tasks/${id}/status`, { status });
+ return response.data;
+ },
+
+ /**
+ * Assign Task to user
+ */
+ assign: async (id: string, assigneeId: string): Promise => {
+ const response = await apiClient.put(`/api/v1/tasks/${id}/assign`, { assigneeId });
+ return response.data;
+ },
+};
+```
+
+### React Query Hooks (`lib/hooks/use-tasks.ts`)
+
+```typescript
+// lib/hooks/use-tasks.ts
+'use client';
+
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { tasksApi } from '@/lib/api/pm';
+import { toast } from 'sonner';
+import { logger } from '@/lib/utils/logger';
+import type { Task, CreateTaskDto, UpdateTaskDto } from '@/types/project';
+
+/**
+ * Get all Tasks for a Story
+ */
+export function useTasks(storyId: string | undefined) {
+ return useQuery({
+ queryKey: ['tasks', storyId],
+ queryFn: () => {
+ if (!storyId) throw new Error('Story ID required');
+ return tasksApi.list(storyId);
+ },
+ enabled: !!storyId,
+ staleTime: 60_000, // Consider fresh for 1 minute
+ });
+}
+
+/**
+ * Get single Task by ID
+ */
+export function useTask(id: string | undefined) {
+ return useQuery({
+ queryKey: ['tasks', id],
+ queryFn: () => {
+ if (!id) throw new Error('Task ID required');
+ return tasksApi.get(id);
+ },
+ enabled: !!id,
+ });
+}
+
+/**
+ * Create new Task with optimistic update
+ */
+export function useCreateTask() {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async (data: CreateTaskDto) => {
+ logger.info('Creating task:', data);
+ return tasksApi.create(data);
+ },
+
+ onMutate: async (newTask) => {
+ // Cancel outgoing queries
+ await queryClient.cancelQueries({ queryKey: ['tasks', newTask.storyId] });
+
+ // Snapshot previous value
+ const previousTasks = queryClient.getQueryData(['tasks', newTask.storyId]);
+
+ // Optimistically update (optional for creates)
+ // Usually we just wait for server response
+
+ return { previousTasks };
+ },
+
+ onSuccess: (newTask, variables) => {
+ logger.info('Task created successfully:', newTask);
+
+ // Invalidate and refetch
+ queryClient.invalidateQueries({ queryKey: ['tasks', variables.storyId] });
+ queryClient.invalidateQueries({ queryKey: ['stories', variables.storyId] }); // Update Story task count
+
+ toast.success('Task created successfully');
+ },
+
+ onError: (error, variables, context) => {
+ logger.error('Failed to create task:', error);
+
+ // Restore previous state if optimistic update was used
+ if (context?.previousTasks) {
+ queryClient.setQueryData(['tasks', variables.storyId], context.previousTasks);
+ }
+
+ toast.error('Failed to create Task');
+ },
+ });
+}
+
+/**
+ * Update Task with optimistic update
+ */
+export function useUpdateTask() {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async ({ id, data }: { id: string; data: UpdateTaskDto }) => {
+ logger.info('Updating task:', id, data);
+ return tasksApi.update(id, data);
+ },
+
+ onMutate: async ({ id, data }) => {
+ // Cancel queries
+ await queryClient.cancelQueries({ queryKey: ['tasks', id] });
+
+ // Snapshot previous
+ const previousTask = queryClient.getQueryData(['tasks', id]);
+
+ // Optimistically update
+ queryClient.setQueryData(['tasks', id], (old) => {
+ if (!old) return old;
+ return { ...old, ...data };
+ });
+
+ return { previousTask };
+ },
+
+ onSuccess: (updatedTask) => {
+ logger.info('Task updated successfully:', updatedTask);
+
+ // Invalidate queries
+ queryClient.invalidateQueries({ queryKey: ['tasks', updatedTask.id] });
+ queryClient.invalidateQueries({ queryKey: ['tasks', updatedTask.storyId] });
+
+ toast.success('Task updated successfully');
+ },
+
+ onError: (error, variables, context) => {
+ logger.error('Failed to update task:', error);
+
+ // Revert optimistic update
+ if (context?.previousTask) {
+ queryClient.setQueryData(['tasks', variables.id], context.previousTask);
+ }
+
+ toast.error('Failed to update Task');
+ },
+ });
+}
+
+/**
+ * Delete Task
+ */
+export function useDeleteTask() {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async (id: string) => {
+ logger.info('Deleting task:', id);
+ return tasksApi.delete(id);
+ },
+
+ onSuccess: (_, deletedId) => {
+ logger.info('Task deleted successfully:', deletedId);
+
+ // Remove from cache
+ queryClient.removeQueries({ queryKey: ['tasks', deletedId] });
+
+ // Invalidate lists
+ queryClient.invalidateQueries({ queryKey: ['tasks'] });
+
+ toast.success('Task deleted successfully');
+ },
+
+ onError: (error) => {
+ logger.error('Failed to delete task:', error);
+ toast.error('Failed to delete Task');
+ },
+ });
+}
+
+/**
+ * Change Task status (for checkbox toggle)
+ */
+export function useChangeTaskStatus() {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async ({ taskId, status }: { taskId: string; status: string }) => {
+ logger.info('Changing task status:', taskId, status);
+ return tasksApi.changeStatus(taskId, status);
+ },
+
+ onMutate: async ({ taskId, status }) => {
+ // Cancel queries
+ await queryClient.cancelQueries({ queryKey: ['tasks', taskId] });
+
+ // Snapshot previous
+ const previousTask = queryClient.getQueryData(['tasks', taskId]);
+
+ // Optimistically update status
+ queryClient.setQueryData(['tasks', taskId], (old) => {
+ if (!old) return old;
+ return { ...old, status };
+ });
+
+ // Also update in list
+ const storyId = previousTask?.storyId;
+ if (storyId) {
+ queryClient.setQueryData(['tasks', storyId], (old) => {
+ if (!old) return old;
+ return old.map((task) =>
+ task.id === taskId ? { ...task, status } : task
+ );
+ });
+ }
+
+ return { previousTask };
+ },
+
+ onSuccess: (updatedTask) => {
+ logger.info('Task status changed successfully:', updatedTask);
+
+ // Invalidate queries
+ queryClient.invalidateQueries({ queryKey: ['tasks', updatedTask.id] });
+ queryClient.invalidateQueries({ queryKey: ['tasks', updatedTask.storyId] });
+ queryClient.invalidateQueries({ queryKey: ['stories', updatedTask.storyId] }); // Update Story progress
+
+ toast.success(`Task marked as ${updatedTask.status}`);
+ },
+
+ onError: (error, variables, context) => {
+ logger.error('Failed to change task status:', error);
+
+ // Revert optimistic update
+ if (context?.previousTask) {
+ queryClient.setQueryData(['tasks', variables.taskId], context.previousTask);
+ }
+
+ toast.error('Failed to update Task status');
+ },
+ });
+}
+
+/**
+ * Assign Task to user
+ */
+export function useAssignTask() {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async ({ taskId, assigneeId }: { taskId: string; assigneeId: string }) => {
+ logger.info('Assigning task:', taskId, assigneeId);
+ return tasksApi.assign(taskId, assigneeId);
+ },
+
+ onSuccess: (updatedTask) => {
+ logger.info('Task assigned successfully:', updatedTask);
+
+ queryClient.invalidateQueries({ queryKey: ['tasks', updatedTask.id] });
+ queryClient.invalidateQueries({ queryKey: ['tasks', updatedTask.storyId] });
+
+ toast.success('Task assigned successfully');
+ },
+
+ onError: (error) => {
+ logger.error('Failed to assign task:', error);
+ toast.error('Failed to assign Task');
+ },
+ });
+}
+```
+
+## Acceptance Criteria
+
+- [ ] Task API client methods added to `lib/api/pm.ts`
+- [ ] All CRUD operations implemented (list, get, create, update, delete)
+- [ ] `use-tasks.ts` hooks file created
+- [ ] `useTasks(storyId)` hook returns Task list for Story
+- [ ] `useCreateTask()` hook creates Task with optimistic update
+- [ ] `useUpdateTask()` hook updates Task with optimistic update
+- [ ] `useDeleteTask()` hook deletes Task
+- [ ] `useChangeTaskStatus()` hook changes status with optimistic update
+- [ ] All hooks include error handling with toast notifications
+- [ ] All hooks include logger integration
+- [ ] Cache invalidation strategies implemented correctly
+- [ ] Optimistic updates provide instant UI feedback
+- [ ] Hooks tested with real API data
+
+## Testing
+
+**Manual Testing**:
+```typescript
+// Test in a React component
+function TestTaskHooks() {
+ const { data: tasks, isLoading } = useTasks('story-123');
+ const createTask = useCreateTask();
+
+ const handleCreate = () => {
+ createTask.mutate({
+ storyId: 'story-123',
+ title: 'Test Task',
+ priority: 'High',
+ estimatedHours: 8,
+ createdBy: 'user-123',
+ });
+ };
+
+ return (
+
+
+ {isLoading &&
Loading...
}
+ {tasks?.map((task) => (
+
{task.title}
+ ))}
+
+ );
+}
+```
+
+**Test Cases**:
+1. Create Task → Verify appears in list immediately (optimistic update)
+2. Update Task → Verify updates instantly
+3. Delete Task → Verify removes from list
+4. Change status → Verify checkbox updates instantly
+5. Error handling → Disconnect internet → Verify error toast shows
+6. Cache invalidation → Create Task → Verify Story task count updates
+
+## Dependencies
+
+**Prerequisites**:
+- Task 1 (API endpoints verified, types created)
+- ✅ React Query configured
+- ✅ apiClient ready (`lib/api/client.ts`)
+- ✅ logger utility (`lib/utils/logger.ts`)
+- ✅ sonner toast library
+
+**Blocks**:
+- Task 3, 4, 5 (components depend on hooks)
+
+## Estimated Time
+
+3 hours
+
+## Notes
+
+**Optimistic Updates**: Provide instant UI feedback by updating cache immediately, then reverting on error. This makes the app feel fast and responsive.
+
+**Cache Invalidation**: When creating/updating/deleting Tasks, also invalidate the parent Story query to update task counts and progress indicators.
+
+**Code Reuse**: Copy patterns from `use-stories.ts` hook - Task hooks are very similar to Story hooks.
diff --git a/docs/plans/sprint_4_story_2_task_3.md b/docs/plans/sprint_4_story_2_task_3.md
new file mode 100644
index 0000000..69b2d2f
--- /dev/null
+++ b/docs/plans/sprint_4_story_2_task_3.md
@@ -0,0 +1,402 @@
+---
+task_id: sprint_4_story_2_task_3
+story_id: sprint_4_story_2
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 2
+created_date: 2025-11-05
+estimated_hours: 4
+---
+
+# Task 3: Implement TaskList Component with Filters and Sorting
+
+## Description
+
+Create the TaskList component that displays all Tasks for a Story, with filtering by status/priority/assignee and sorting capabilities. This component is the container for Task management UI.
+
+## What to Do
+
+1. Create `components/projects/task-list.tsx`
+2. Display Task count badge (e.g., "Tasks (8)")
+3. Add "Add Task" button to open inline form
+4. Implement Task filters (All, Todo, InProgress, Done)
+5. Implement Task sorting (Priority, Status, Created date, Assignee)
+6. Show empty state when no Tasks exist
+7. Render TaskCard components for each Task
+8. Add loading skeleton during data fetch
+9. Integrate with useTasks hook
+10. Test with various filter/sort combinations
+
+## Files to Create
+
+- `components/projects/task-list.tsx` (new, ~300-400 lines)
+
+## Implementation Details
+
+```typescript
+// components/projects/task-list.tsx
+'use client';
+
+import { useState, useMemo } from 'react';
+import { Plus, Filter, ArrowUpDown } from 'lucide-react';
+import { Card } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select';
+import { Skeleton } from '@/components/ui/skeleton';
+import { TaskCard } from './task-card';
+import { TaskForm } from './task-form';
+import { useTasks } from '@/lib/hooks/use-tasks';
+import type { Task, WorkItemStatus, WorkItemPriority } from '@/types/project';
+
+interface TaskListProps {
+ storyId: string;
+ readonly?: boolean;
+}
+
+type FilterStatus = 'All' | WorkItemStatus;
+type FilterPriority = 'All' | WorkItemPriority;
+type SortOption = 'priority' | 'status' | 'createdAt' | 'assignee';
+
+export function TaskList({ storyId, readonly = false }: TaskListProps) {
+ // State
+ const [showAddForm, setShowAddForm] = useState(false);
+ const [filterStatus, setFilterStatus] = useState('All');
+ const [filterPriority, setFilterPriority] = useState('All');
+ const [sortBy, setSortBy] = useState('priority');
+
+ // Fetch Tasks
+ const { data: tasks = [], isLoading, error } = useTasks(storyId);
+
+ // Filter and sort Tasks
+ const filteredAndSortedTasks = useMemo(() => {
+ let result = [...tasks];
+
+ // Apply status filter
+ if (filterStatus !== 'All') {
+ result = result.filter((task) => task.status === filterStatus);
+ }
+
+ // Apply priority filter
+ if (filterPriority !== 'All') {
+ result = result.filter((task) => task.priority === filterPriority);
+ }
+
+ // Apply sorting
+ result.sort((a, b) => {
+ switch (sortBy) {
+ case 'priority':
+ return getPriorityValue(b.priority) - getPriorityValue(a.priority);
+ case 'status':
+ return getStatusValue(a.status) - getStatusValue(b.status);
+ case 'createdAt':
+ return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
+ case 'assignee':
+ return (a.assigneeId || '').localeCompare(b.assigneeId || '');
+ default:
+ return 0;
+ }
+ });
+
+ return result;
+ }, [tasks, filterStatus, filterPriority, sortBy]);
+
+ // Task count
+ const taskCount = tasks.length;
+ const completedCount = tasks.filter((t) => t.status === 'Done').length;
+
+ // Loading state
+ if (isLoading) {
+ return ;
+ }
+
+ // Error state
+ if (error) {
+ return (
+
+ Failed to load Tasks. Please try again.
+
+ );
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
Tasks
+
+ {taskCount} total
+
+ {completedCount > 0 && (
+
+ {completedCount} done
+
+ )}
+
+
+ {!readonly && (
+
+ )}
+
+
+ {/* Filters and Sorting */}
+ {taskCount > 0 && (
+
+
+
+ Filter:
+
+
+
+
+
+
+
+
+
Sort:
+
+
+
+ )}
+
+ {/* Add Task Form */}
+ {showAddForm && (
+
+ setShowAddForm(false)}
+ onCancel={() => setShowAddForm(false)}
+ />
+
+ )}
+
+ {/* Task List */}
+ {filteredAndSortedTasks.length > 0 ? (
+
+ {filteredAndSortedTasks.map((task) => (
+
+ ))}
+
+ ) : (
+
{
+ setFilterStatus('All');
+ setFilterPriority('All');
+ }}
+ onAddTask={() => setShowAddForm(true)}
+ readonly={readonly}
+ />
+ )}
+
+ );
+}
+
+// Helper functions for sorting
+function getPriorityValue(priority: WorkItemPriority): number {
+ const values = { Low: 1, Medium: 2, High: 3, Critical: 4 };
+ return values[priority] || 0;
+}
+
+function getStatusValue(status: WorkItemStatus): number {
+ const values = { Backlog: 1, Todo: 2, InProgress: 3, Done: 4 };
+ return values[status] || 0;
+}
+
+// Empty state component
+function EmptyState({
+ hasFilters,
+ onReset,
+ onAddTask,
+ readonly,
+}: {
+ hasFilters: boolean;
+ onReset: () => void;
+ onAddTask: () => void;
+ readonly: boolean;
+}) {
+ if (hasFilters) {
+ return (
+
+
+ No Tasks match the selected filters.
+
+
+
+ );
+ }
+
+ return (
+
+ ☑️
+ No Tasks Yet
+
+ Break down this Story into technical Tasks to track implementation progress.
+
+ {!readonly && (
+
+ )}
+
+ );
+}
+
+// Loading skeleton
+function TaskListSkeleton() {
+ return (
+
+ );
+}
+```
+
+## Acceptance Criteria
+
+- [ ] TaskList component displays all Tasks for Story
+- [ ] Task count badge shows total and completed count
+- [ ] "Add Task" button opens inline Task form
+- [ ] Status filter works (All, Backlog, Todo, InProgress, Done)
+- [ ] Priority filter works (All, Low, Medium, High, Critical)
+- [ ] Sorting works (Priority, Status, Created date, Assignee)
+- [ ] Filters and sorting can be combined
+- [ ] Empty state displays when no Tasks exist
+- [ ] Empty state with filters shows "Reset Filters" button
+- [ ] Loading skeleton displays during data fetch
+- [ ] Error state displays on fetch failure
+- [ ] Component integrates with useTasks hook
+- [ ] Readonly mode hides "Add Task" button
+- [ ] Performance: Filters/sorts without lag (<100ms)
+
+## Testing
+
+**Manual Testing**:
+1. Navigate to Story detail page
+2. Verify Task count badge shows correctly (e.g., "8 total, 3 done")
+3. Click "Add Task" → Verify inline form appears
+4. Create 5 Tasks with different statuses and priorities
+5. Test status filter → Select "Done" → Verify only Done Tasks show
+6. Test priority filter → Select "High" → Verify only High priority Tasks show
+7. Test combined filters → Status: InProgress + Priority: High
+8. Test sorting → Sort by Priority → Verify High/Critical at top
+9. Test sorting → Sort by Status → Verify Backlog → Todo → InProgress → Done
+10. Reset filters → Verify all Tasks show again
+11. Test empty state (Story with no Tasks) → Verify helpful message
+
+**Unit Test**:
+```typescript
+import { render, screen, fireEvent } from '@testing-library/react';
+import { TaskList } from './task-list';
+
+describe('TaskList', () => {
+ const mockTasks = [
+ { id: '1', title: 'Task 1', status: 'Todo', priority: 'High' },
+ { id: '2', title: 'Task 2', status: 'Done', priority: 'Low' },
+ { id: '3', title: 'Task 3', status: 'InProgress', priority: 'Critical' },
+ ];
+
+ it('renders task count badge', () => {
+ render();
+ expect(screen.getByText('3 total')).toBeInTheDocument();
+ expect(screen.getByText('1 done')).toBeInTheDocument();
+ });
+
+ it('filters tasks by status', () => {
+ render();
+ fireEvent.click(screen.getByText('All Status'));
+ fireEvent.click(screen.getByText('Done'));
+ expect(screen.getAllByTestId('task-card')).toHaveLength(1);
+ expect(screen.getByText('Task 2')).toBeInTheDocument();
+ });
+
+ it('sorts tasks by priority', () => {
+ render();
+ const tasks = screen.getAllByTestId('task-card');
+ expect(tasks[0]).toHaveTextContent('Task 3'); // Critical first
+ expect(tasks[1]).toHaveTextContent('Task 1'); // High second
+ });
+});
+```
+
+## Dependencies
+
+**Prerequisites**:
+- Task 2 (useTasks hook must exist)
+- TaskCard component (Task 4 - can use placeholder initially)
+- TaskForm component (Task 5 - can use placeholder initially)
+- ✅ shadcn/ui Card, Button, Badge, Select components
+
+**Blocks**:
+- Task 6 (Story detail page integration)
+
+## Estimated Time
+
+4 hours
+
+## Notes
+
+**Performance**: Use `useMemo` for filtering/sorting to avoid recalculating on every render. With 100+ Tasks, this prevents performance issues.
+
+**Empty State**: Different empty states for "no Tasks" vs "no Tasks matching filters" improve UX.
+
+**Progressive Enhancement**: Component works with TaskCard and TaskForm placeholders initially. Replace with real components in Tasks 4 and 5.
diff --git a/docs/plans/sprint_4_story_2_task_4.md b/docs/plans/sprint_4_story_2_task_4.md
new file mode 100644
index 0000000..0858fe7
--- /dev/null
+++ b/docs/plans/sprint_4_story_2_task_4.md
@@ -0,0 +1,28 @@
+---
+task_id: sprint_4_story_2_task_4
+story_id: sprint_4_story_2
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 2
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 4: Implement TaskCard Component with Checkbox Status Toggle
+
+## Description
+
+Create the TaskCard component that displays individual Task information with a checkbox for quick status toggling.
+
+## Acceptance Criteria
+
+- [ ] TaskCard displays Task title, priority, estimated hours
+- [ ] Checkbox shows correct state (checked = Done, unchecked = Todo/InProgress)
+- [ ] Clicking checkbox toggles Task status
+- [ ] Optimistic UI update for instant feedback
+- [ ] Hover effect highlights card
+
+## Estimated Time
+
+3 hours
diff --git a/docs/plans/sprint_4_story_2_task_5.md b/docs/plans/sprint_4_story_2_task_5.md
new file mode 100644
index 0000000..f430d03
--- /dev/null
+++ b/docs/plans/sprint_4_story_2_task_5.md
@@ -0,0 +1,29 @@
+---
+task_id: sprint_4_story_2_task_5
+story_id: sprint_4_story_2
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 2
+created_date: 2025-11-05
+estimated_hours: 3
+---
+
+# Task 5: Implement Inline TaskForm for Task Creation
+
+## Description
+
+Create the inline TaskForm component for creating new Tasks without leaving the Story detail page.
+
+## Acceptance Criteria
+
+- [ ] Form displays title, description, priority, estimated hours fields
+- [ ] Title field is required with validation
+- [ ] Submitting form creates Task successfully
+- [ ] Form resets after successful creation
+- [ ] Cancel button closes form
+- [ ] Success and error toast notifications
+
+## Estimated Time
+
+3 hours
diff --git a/docs/plans/sprint_4_story_2_task_6.md b/docs/plans/sprint_4_story_2_task_6.md
new file mode 100644
index 0000000..115594d
--- /dev/null
+++ b/docs/plans/sprint_4_story_2_task_6.md
@@ -0,0 +1,30 @@
+---
+task_id: sprint_4_story_2_task_6
+story_id: sprint_4_story_2
+sprint_id: sprint_4
+status: not_started
+type: frontend
+assignee: Frontend Developer 2
+created_date: 2025-11-05
+estimated_hours: 2
+---
+
+# Task 6: Integrate Task Management into Story Detail Page
+
+## Description
+
+Integrate the complete Task management system into the Story detail page and test full functionality.
+
+## Acceptance Criteria
+
+- [ ] TaskList component integrated into Story detail page
+- [ ] Task count badge shows in Story header
+- [ ] Full Task CRUD operations working
+- [ ] Filters and sorting functional
+- [ ] Checkbox status toggle working
+- [ ] E2E tests passing
+- [ ] No console errors
+
+## Estimated Time
+
+2 hours
diff --git a/docs/plans/sprint_4_story_3.md b/docs/plans/sprint_4_story_3.md
new file mode 100644
index 0000000..488ddf4
--- /dev/null
+++ b/docs/plans/sprint_4_story_3.md
@@ -0,0 +1,58 @@
+---
+story_id: sprint_4_story_3
+sprint: sprint_4
+priority: P1
+status: not_started
+story_points: 3
+estimated_days: 2
+created_date: 2025-11-05
+assignee: Frontend Team
+---
+
+# Story 3: Enhanced Story Form
+
+**Sprint**: Sprint 4
+**Priority**: P1 (High)
+**Estimated**: 2 days
+**Owner**: Frontend Team
+
+## Description
+
+Enhance the existing Story form component to include UX-designed fields: Acceptance Criteria (checkbox list), Assignee selector, Tags/Labels (multi-select), and Story Points. This improves Story planning capabilities and aligns with the comprehensive UX design.
+
+## User Story
+
+**As a** product manager,
+**I want** to add acceptance criteria, assignee, tags, and story points when creating Stories,
+**So that** I can better plan and communicate Story requirements.
+
+## Acceptance Criteria
+
+- [ ] Form includes Acceptance Criteria field (dynamic checkbox list)
+- [ ] Form includes Assignee selector (searchable dropdown showing users)
+- [ ] Form includes Tags field (multi-select labels)
+- [ ] Form includes Story Points field (optional numeric)
+- [ ] Acceptance criteria can be added/removed dynamically
+- [ ] Tags support multi-select
+- [ ] Form validation works for all fields
+- [ ] Backward compatible with existing Stories (missing fields are optional)
+- [ ] Form saves correctly with all new fields
+
+## Tasks
+
+- [ ] Task 1: Add Acceptance Criteria editor component
+- [ ] Task 2: Implement Assignee selector component
+- [ ] Task 3: Add Tags/Labels multi-select component
+- [ ] Task 4: Add Story Points field and update schema
+- [ ] Task 5: Integrate all new fields into Story form
+- [ ] Task 6: Update Story types and API client
+
+**Progress**: 0/6 tasks completed
+
+## Estimated Time
+
+2 days (16 hours)
+
+---
+
+**Created**: 2025-11-05 by Frontend Agent
diff --git a/docs/plans/sprint_4_story_4.md b/docs/plans/sprint_4_story_4.md
new file mode 100644
index 0000000..4c083b0
--- /dev/null
+++ b/docs/plans/sprint_4_story_4.md
@@ -0,0 +1,57 @@
+---
+story_id: sprint_4_story_4
+sprint: sprint_4
+priority: P1
+status: not_started
+story_points: 3
+estimated_days: 2
+created_date: 2025-11-05
+assignee: Frontend Team
+---
+
+# Story 4: Quick Add Story Workflow
+
+**Sprint**: Sprint 4
+**Priority**: P1 (High)
+**Estimated**: 2 days
+**Owner**: Frontend Team
+
+## Description
+
+Implement a Quick Add Story workflow with an inline form that requires only title and priority. This enables rapid Story creation for batch planning sessions without the overhead of the full form dialog.
+
+## User Story
+
+**As a** product manager,
+**I want** to quickly create multiple Stories with just title and priority,
+**So that** I can rapidly plan Epics during brainstorming sessions.
+
+## Acceptance Criteria
+
+- [ ] Quick Add button appears at top of Stories list in Epic detail page
+- [ ] Clicking button shows inline form (not dialog)
+- [ ] Form requires only title and priority (minimal fields)
+- [ ] Pressing Enter key submits form
+- [ ] Form resets and stays open after successful creation (batch creation)
+- [ ] Keyboard shortcut (Cmd/Ctrl + N) opens Quick Add form
+- [ ] "Add & Create Tasks" button variant navigates to Story detail page
+- [ ] Form animations smooth and performant
+- [ ] Success toast notifications shown
+
+## Tasks
+
+- [ ] Task 1: Create QuickAddStory component with inline form
+- [ ] Task 2: Add keyboard shortcut handler (Cmd/Ctrl + N)
+- [ ] Task 3: Implement auto-reset and batch creation flow
+- [ ] Task 4: Add "Add & Create Tasks" button and navigation
+- [ ] Task 5: Integrate into Epic detail page
+
+**Progress**: 0/5 tasks completed
+
+## Estimated Time
+
+2 days (16 hours)
+
+---
+
+**Created**: 2025-11-05 by Frontend Agent
diff --git a/docs/plans/sprint_4_story_5.md b/docs/plans/sprint_4_story_5.md
new file mode 100644
index 0000000..374a0a2
--- /dev/null
+++ b/docs/plans/sprint_4_story_5.md
@@ -0,0 +1,55 @@
+---
+story_id: sprint_4_story_5
+sprint: sprint_4
+priority: P2
+status: not_started
+story_points: 2
+estimated_days: 1
+created_date: 2025-11-05
+assignee: Frontend Team
+---
+
+# Story 5: Story Card Component
+
+**Sprint**: Sprint 4
+**Priority**: P2 (Medium)
+**Estimated**: 1 day
+**Owner**: Frontend Team
+
+## Description
+
+Create a reusable StoryCard component with three variants (list, kanban, compact) to replace inline Story display code and improve code maintainability. This component will be used across Epic detail page, Kanban board, and Story lists.
+
+## User Story
+
+**As a** developer,
+**I want** a reusable Story card component,
+**So that** Story display is consistent across the app and easier to maintain.
+
+## Acceptance Criteria
+
+- [ ] StoryCard component works in three variants (list, kanban, compact)
+- [ ] Visual states implemented (default, hover, selected, dragging)
+- [ ] Quick actions menu appears on hover (Edit, Delete, Duplicate)
+- [ ] Task count indicator shows (e.g., "5 tasks")
+- [ ] Component shows all relevant metadata (status, priority, assignee, time)
+- [ ] Component is reusable across different views
+- [ ] Performance optimized with React.memo
+- [ ] TypeScript types fully defined
+
+## Tasks
+
+- [ ] Task 1: Create StoryCard component with three variants
+- [ ] Task 2: Implement visual states and hover effects
+- [ ] Task 3: Add Task count indicator and quick actions
+- [ ] Task 4: Optimize with React.memo and replace existing Story cards
+
+**Progress**: 0/4 tasks completed
+
+## Estimated Time
+
+1 day (8 hours)
+
+---
+
+**Created**: 2025-11-05 by Frontend Agent
diff --git a/docs/plans/sprint_4_story_6.md b/docs/plans/sprint_4_story_6.md
new file mode 100644
index 0000000..5bc3dec
--- /dev/null
+++ b/docs/plans/sprint_4_story_6.md
@@ -0,0 +1,58 @@
+---
+story_id: sprint_4_story_6
+sprint: sprint_4
+priority: P2
+status: not_started
+story_points: 3
+estimated_days: 2
+created_date: 2025-11-05
+assignee: Frontend Team
+---
+
+# Story 6: Kanban Story Creation Enhancement (Optional)
+
+**Sprint**: Sprint 4
+**Priority**: P2 (Optional - Stretch Goal)
+**Estimated**: 2 days
+**Owner**: Frontend Team
+
+## Description
+
+Add contextual Story creation directly from Epic cards in the Kanban board. This allows users to create Stories without leaving the Kanban view, maintaining context and improving workflow efficiency.
+
+## User Story
+
+**As a** product manager,
+**I want** to create Stories directly from Epic cards in Kanban,
+**So that** I can quickly add Stories while planning without leaving the board view.
+
+## Acceptance Criteria
+
+- [ ] Hovering over Epic card shows "+ Add Story" button
+- [ ] Clicking button opens inline Story form below Epic card
+- [ ] Form is context-bound (Epic pre-selected, read-only)
+- [ ] Created Story appears in correct Kanban column by status
+- [ ] Epic Story count updates in real-time
+- [ ] Form slide-in animation smooth and intuitive
+- [ ] Can cancel or close form easily (Cancel button, outside click, ESC key)
+
+## Tasks
+
+- [ ] Task 1: Enhance Epic card with "+ Add Story" action on hover
+- [ ] Task 2: Create inline Story form for Kanban context
+- [ ] Task 3: Implement form slide-in animation
+- [ ] Task 4: Integrate real-time Epic Story count updates
+
+**Progress**: 0/4 tasks completed
+
+## Estimated Time
+
+2 days (16 hours)
+
+## Notes
+
+**Status**: Optional stretch goal. Implement only if Stories 1-5 complete ahead of schedule. If time is tight, defer to future sprint.
+
+---
+
+**Created**: 2025-11-05 by Frontend Agent