'use client'; import { use, useState } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { ArrowLeft, Edit, Trash2, Loader2, Clock, Calendar, User, Layers, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useStory, useUpdateStory, useDeleteStory, useChangeStoryStatus } from '@/lib/hooks/use-stories'; import { useEpic } from '@/lib/hooks/use-epics'; import { useProject } from '@/lib/hooks/use-projects'; import { StoryForm } from '@/components/projects/story-form'; import { TaskList } from '@/components/tasks/task-list'; import { formatDistanceToNow } from 'date-fns'; import { toast } from 'sonner'; import type { WorkItemStatus, WorkItemPriority } from '@/types/project'; interface StoryDetailPageProps { params: Promise<{ id: string }>; } export default function StoryDetailPage({ params }: StoryDetailPageProps) { const { id: storyId } = use(params); const router = useRouter(); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const { data: story, isLoading: storyLoading, error: storyError } = useStory(storyId); const { data: epic, isLoading: epicLoading } = useEpic(story?.epicId || ''); const { data: project, isLoading: projectLoading } = useProject(story?.projectId || ''); const updateStory = useUpdateStory(); const deleteStory = useDeleteStory(); const changeStatus = useChangeStoryStatus(); const handleDeleteStory = async () => { try { await deleteStory.mutateAsync(storyId); toast.success('Story deleted successfully'); // Navigate back to epic detail page router.push(`/epics/${story?.epicId}`); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to delete story'; toast.error(message); } }; const handleStatusChange = async (status: WorkItemStatus) => { if (!story) return; try { await changeStatus.mutateAsync({ id: storyId, status }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to update status'; toast.error(message); } }; const handlePriorityChange = async (priority: WorkItemPriority) => { if (!story) return; try { await updateStory.mutateAsync({ id: storyId, data: { priority }, }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to update priority'; toast.error(message); } }; const getStatusColor = (status: WorkItemStatus) => { switch (status) { case 'Backlog': return 'secondary'; case 'Todo': return 'outline'; case 'InProgress': return 'default'; case 'Done': return 'success' as any; default: return 'secondary'; } }; const getPriorityColor = (priority: WorkItemPriority) => { switch (priority) { case 'Low': return 'bg-blue-100 text-blue-700 hover:bg-blue-100'; case 'Medium': return 'bg-yellow-100 text-yellow-700 hover:bg-yellow-100'; case 'High': return 'bg-orange-100 text-orange-700 hover:bg-orange-100'; case 'Critical': return 'bg-red-100 text-red-700 hover:bg-red-100'; default: return 'secondary'; } }; // Loading state if (storyLoading || epicLoading || projectLoading) { return (
{story.description}
No description
)}Created
{formatDistanceToNow(new Date(story.createdAt), { addSuffix: true })}
Updated
{formatDistanceToNow(new Date(story.updatedAt), { addSuffix: true })}