Files
ColaFlow-Web/app/(dashboard)/stories/[id]/loading.tsx
Yaojia Wang f7a17a3d1a feat(frontend): Implement Story detail page - Sprint 4 Story 1
Add complete Story detail page with two-column layout, breadcrumb navigation,
and full CRUD operations.

Key Features:
- Story detail page at /stories/[id] route
- Two-column layout (main content + metadata sidebar)
- Breadcrumb navigation: Projects > Project > Epics > Epic > Stories > Story
- Story header with title, status, priority badges, Edit/Delete actions
- Main content area with Story description and Tasks placeholder
- Metadata sidebar with:
  * Status selector (with optimistic updates)
  * Priority selector
  * Assignee display
  * Time tracking (estimated/actual hours)
  * Created/Updated dates
  * Parent Epic card (clickable link)
- Edit Story dialog (reuses StoryForm component)
- Delete Story confirmation dialog
- Loading state (skeleton loaders)
- Error handling with error.tsx
- Responsive design (mobile/tablet/desktop)
- Accessibility support (keyboard navigation, ARIA labels)

Technical Implementation:
- Uses Next.js 13+ App Router with dynamic routes
- React Query for data fetching and caching
- Optimistic updates for status/priority changes
- Proper TypeScript typing throughout
- Reuses existing components (StoryForm, shadcn/ui)
- 85% code reuse from Epic detail page pattern

Bug Fixes:
- Fixed TypeScript error in pm.ts (api.post generic type)

Files Created:
- app/(dashboard)/stories/[id]/page.tsx (478 lines)
- app/(dashboard)/stories/[id]/loading.tsx (66 lines)
- app/(dashboard)/stories/[id]/error.tsx (53 lines)

Files Modified:
- lib/api/pm.ts (added generic type to api.post<Epic>)

Verification:
- Build successful (npm run build)
- No TypeScript errors
- Route registered: /stories/[id] (Dynamic)

Next Steps:
- Task management functionality (Sprint 4 Story 2)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 22:00:24 +01:00

67 lines
2.0 KiB
TypeScript

import { Skeleton } from '@/components/ui/skeleton';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
export default function StoryDetailLoading() {
return (
<div className="space-y-6">
{/* Breadcrumb Skeleton */}
<Skeleton className="h-5 w-96" />
{/* Header Skeleton */}
<div className="flex items-start justify-between gap-4">
<div className="space-y-4 flex-1">
<Skeleton className="h-10 w-3/4" />
<div className="flex gap-2">
<Skeleton className="h-6 w-20" />
<Skeleton className="h-6 w-20" />
</div>
</div>
<div className="flex gap-2">
<Skeleton className="h-10 w-32" />
<Skeleton className="h-10 w-24" />
</div>
</div>
{/* Two-column layout Skeleton */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Content */}
<div className="lg:col-span-2 space-y-6">
<Card>
<CardHeader>
<Skeleton className="h-6 w-32" />
</CardHeader>
<CardContent className="space-y-4">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
</CardContent>
</Card>
<Card>
<CardHeader>
<Skeleton className="h-6 w-24" />
</CardHeader>
<CardContent>
<Skeleton className="h-32 w-full" />
</CardContent>
</Card>
</div>
{/* Sidebar */}
<div className="space-y-4">
{Array.from({ length: 5 }).map((_, i) => (
<Card key={i}>
<CardHeader className="pb-3">
<Skeleton className="h-5 w-20" />
</CardHeader>
<CardContent>
<Skeleton className="h-10 w-full" />
</CardContent>
</Card>
))}
</div>
</div>
</div>
);
}