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>
10 KiB
task_id, story_id, sprint_id, status, type, assignee, created_date, estimated_hours
| task_id | story_id | sprint_id | status | type | assignee | created_date | estimated_hours |
|---|---|---|---|---|---|---|---|
| sprint_4_story_1_task_5 | sprint_4_story_1 | sprint_4 | not_started | frontend | Frontend Developer 1 | 2025-11-05 | 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
- Add state management for dialog visibility (Edit dialog, Delete dialog)
- Integrate existing Story form component for Edit action
- Create Delete confirmation dialog
- Show cascade warning if Story has Tasks (list affected Tasks)
- Implement delete mutation with error handling
- Add success/error toast notifications
- Handle post-delete navigation (redirect to Epic detail page)
- Test Edit and Delete flows
Files to Modify
app/(dashboard)/stories/[id]/page.tsx(modify, ~80 lines added for dialogs)
Implementation Details
// 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:
- Click Edit button → Verify dialog opens
- Verify form pre-filled with current Story data
- Change title → Save → Verify title updates on page
- Change description → Save → Verify description updates
- Change priority → Save → Verify priority badge updates
- Test validation → Submit empty title → Verify error message
- Click Cancel → Verify dialog closes without saving
- Press ESC → Verify dialog closes
Delete Story Flow:
- Click Delete button → Verify confirmation dialog opens
- Verify Story title displayed in confirmation message
- If Story has Tasks → Verify cascade warning shows with Task list
- Click Cancel → Verify dialog closes, Story not deleted
- Click Delete Story → Verify Story deleted
- Verify redirected to Epic detail page
- Verify success toast shown
- Go to Epic page → Verify Story no longer in list
Error Handling:
- Disconnect internet
- Try to edit Story → Verify error toast shown
- Try to delete Story → Verify error toast shown
- Verify user stays on page after error
E2E Test:
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.