feat(frontend): Create Sprint 4 Stories and Tasks for Story Management

Created comprehensive Story and Task files for Sprint 4 frontend implementation:

Story 1: Story Detail Page Foundation (P0 Critical - 3 days)
- 6 tasks: route creation, header, sidebar, data loading, Edit/Delete, responsive design
- Fixes critical 404 error when clicking Story cards
- Two-column layout consistent with Epic detail page

Story 2: Task Management in Story Detail (P0 Critical - 2 days)
- 6 tasks: API verification, hooks, TaskList, TaskCard, TaskForm, integration
- Complete Task CRUD with checkbox status toggle
- Filters, sorting, and optimistic UI updates

Story 3: Enhanced Story Form (P1 High - 2 days)
- 6 tasks: acceptance criteria, assignee selector, tags, story points, integration
- Aligns with UX design specification
- Backward compatible with existing Stories

Story 4: Quick Add Story Workflow (P1 High - 2 days)
- 5 tasks: inline form, keyboard shortcuts, batch creation, navigation
- Rapid Story creation with minimal fields
- Keyboard shortcut (Cmd/Ctrl + N)

Story 5: Story Card Component (P2 Medium - 1 day)
- 4 tasks: component variants, visual states, Task count, optimization
- Reusable component with list/kanban/compact variants
- React.memo optimization

Story 6: Kanban Story Creation Enhancement (P2 Optional - 2 days)
- 4 tasks: Epic card enhancement, inline form, animation, real-time updates
- Contextual Story creation from Kanban
- Stretch goal - implement only if ahead of schedule

Total: 6 Stories, 31 Tasks, 12 days estimated
Priority breakdown: P0 (2), P1 (2), P2 (2 optional)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-05 21:49:57 +01:00
parent b3c92042ed
commit 88d6413f81
18 changed files with 3629 additions and 0 deletions

View File

@@ -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 (
<div className="container mx-auto px-4 py-6 max-w-7xl">
{/* ... existing content ... */}
<StoryHeader
story={story}
onEdit={() => setIsEditDialogOpen(true)}
onDelete={() => setIsDeleteDialogOpen(true)}
/>
{/* ... rest of content ... */}
{/* Edit Story Dialog */}
<Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Edit Story</DialogTitle>
<DialogDescription>
Update Story details. Changes will be saved immediately.
</DialogDescription>
</DialogHeader>
<StoryForm
mode="edit"
initialData={{
epicId: story.epicId,
projectId: story.projectId,
title: story.title,
description: story.description,
priority: story.priority,
estimatedHours: story.estimatedHours,
}}
onSubmit={handleEditStory}
onCancel={() => setIsEditDialogOpen(false)}
isSubmitting={updateStory.isPending}
/>
</DialogContent>
</Dialog>
{/* Delete Story Confirmation Dialog */}
<AlertDialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-destructive" />
Delete Story?
</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete <strong>{story?.title}</strong> and all associated data.
This action cannot be undone.
{/* Cascade warning if Tasks exist */}
{tasks.length > 0 && (
<div className="mt-4 p-3 bg-destructive/10 border border-destructive/20 rounded-md">
<p className="font-medium text-destructive mb-2">
⚠️ This Story has {tasks.length} Task{tasks.length > 1 ? 's' : ''} that will also be deleted:
</p>
<ul className="list-disc list-inside text-sm space-y-1">
{tasks.slice(0, 5).map((task) => (
<li key={task.id}>{task.title}</li>
))}
{tasks.length > 5 && (
<li className="italic">... and {tasks.length - 5} more</li>
)}
</ul>
</div>
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={deleteStory.isPending}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
onClick={handleDeleteStory}
disabled={deleteStory.isPending}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{deleteStory.isPending && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}
Delete Story
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}
```
## 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.