---
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.
