Files
ColaFlow-Web/app/(dashboard)/api-test/page.tsx
Yaojia Wang 04ba00d108 fix(frontend): Align Epic field names with backend API
Fix frontend-backend API field mismatches for Epic entity by:
1. Changed Epic.title to Epic.name in type definitions
2. Added Epic.createdBy field (required by backend)
3. Updated all Epic references from epic.title to epic.name
4. Fixed Epic form to use name field and include createdBy

Files modified:
- types/project.ts: Updated Epic, CreateEpicDto, UpdateEpicDto interfaces
- components/epics/epic-form.tsx: Fixed defaultValues to use epic.name
- components/projects/hierarchy-tree.tsx: Replaced epic.title with epic.name
- components/projects/story-form.tsx: Fixed epic dropdown to show epic.name
- app/(dashboard)/projects/[id]/epics/page.tsx: Display epic.name in list
- app/(dashboard)/projects/[id]/page.tsx: Display epic.name in preview
- app/(dashboard)/api-test/page.tsx: Display epic.name in test page

This resolves the 400 Bad Request error when creating Epics caused by
missing 'Name' field (was sending 'title' instead) and missing 'CreatedBy' field.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 13:30:48 +01:00

204 lines
6.9 KiB
TypeScript

'use client';
import { useProjects } from '@/lib/hooks/use-projects';
import { useEpics } from '@/lib/hooks/use-epics';
import { useStories } from '@/lib/hooks/use-stories';
import { useTasks } from '@/lib/hooks/use-tasks';
import { Card } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { Badge } from '@/components/ui/badge';
export default function ApiTestPage() {
const { data: projects, isLoading: projectsLoading, error: projectsError } = useProjects();
const { data: epics, isLoading: epicsLoading, error: epicsError } = useEpics();
const { data: stories, isLoading: storiesLoading, error: storiesError } = useStories();
const { data: tasks, isLoading: tasksLoading, error: tasksError } = useTasks();
return (
<div className="container py-6 space-y-8">
<div>
<h1 className="text-3xl font-bold mb-2">API Connection Test</h1>
<p className="text-muted-foreground">
This page tests the connection to ProjectManagement API endpoints
</p>
</div>
{/* Projects Section */}
<Section
title="Projects"
count={projects?.length}
loading={projectsLoading}
error={projectsError}
>
{projects && projects.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{projects.map((project) => (
<Card key={project.id} className="p-4">
<h3 className="font-semibold">{project.name}</h3>
<p className="text-sm text-muted-foreground">{project.key}</p>
{project.description && (
<p className="text-sm text-gray-600 mt-2">{project.description}</p>
)}
<div className="mt-2 flex gap-2">
<Badge variant="outline">ID: {project.id.substring(0, 8)}...</Badge>
</div>
</Card>
))}
</div>
) : (
<EmptyState message="No projects found" />
)}
</Section>
{/* Epics Section */}
<Section
title="Epics"
count={epics?.length}
loading={epicsLoading}
error={epicsError}
>
{epics && epics.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{epics.map((epic) => (
<Card key={epic.id} className="p-4">
<h3 className="font-semibold">{epic.name}</h3>
{epic.description && (
<p className="text-sm text-gray-600 mt-1">{epic.description}</p>
)}
<div className="mt-2 flex gap-2 flex-wrap">
<Badge variant="default">{epic.status}</Badge>
<Badge variant="outline">{epic.priority}</Badge>
{epic.estimatedHours && (
<Badge variant="secondary">{epic.estimatedHours}h</Badge>
)}
</div>
</Card>
))}
</div>
) : (
<EmptyState message="No epics found" />
)}
</Section>
{/* Stories Section */}
<Section
title="Stories"
count={stories?.length}
loading={storiesLoading}
error={storiesError}
>
{stories && stories.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{stories.map((story) => (
<Card key={story.id} className="p-4">
<h3 className="font-semibold">{story.title}</h3>
{story.description && (
<p className="text-sm text-gray-600 mt-1">{story.description}</p>
)}
<div className="mt-2 flex gap-2 flex-wrap">
<Badge variant="default">{story.status}</Badge>
<Badge variant="outline">{story.priority}</Badge>
{story.estimatedHours && (
<Badge variant="secondary">{story.estimatedHours}h</Badge>
)}
</div>
</Card>
))}
</div>
) : (
<EmptyState message="No stories found" />
)}
</Section>
{/* Tasks Section */}
<Section
title="Tasks"
count={tasks?.length}
loading={tasksLoading}
error={tasksError}
>
{tasks && tasks.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{tasks.map((task) => (
<Card key={task.id} className="p-4">
<h3 className="font-semibold">{task.title}</h3>
{task.description && (
<p className="text-sm text-gray-600 mt-1">{task.description}</p>
)}
<div className="mt-2 flex gap-2 flex-wrap">
<Badge variant="default">{task.status}</Badge>
<Badge variant="outline">{task.priority}</Badge>
{task.estimatedHours && (
<Badge variant="secondary">{task.estimatedHours}h</Badge>
)}
</div>
</Card>
))}
</div>
) : (
<EmptyState message="No tasks found" />
)}
</Section>
</div>
);
}
// Helper Components
interface SectionProps {
title: string;
count?: number;
loading: boolean;
error: any;
children: React.ReactNode;
}
function Section({ title, count, loading, error, children }: SectionProps) {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold">
{title}
{count !== undefined && (
<span className="ml-2 text-muted-foreground text-lg">({count})</span>
)}
</h2>
{loading && <Badge variant="secondary">Loading...</Badge>}
{error && <Badge variant="destructive">Error</Badge>}
</div>
{loading ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Skeleton className="h-32" />
<Skeleton className="h-32" />
<Skeleton className="h-32" />
</div>
) : error ? (
<Card className="p-6 border-destructive">
<h3 className="text-lg font-semibold text-destructive mb-2">Error Loading {title}</h3>
<p className="text-sm text-muted-foreground">
{error.message || 'Unknown error occurred'}
</p>
{error.response?.status && (
<p className="text-sm text-muted-foreground mt-2">
Status Code: {error.response.status}
</p>
)}
</Card>
) : (
children
)}
</div>
);
}
function EmptyState({ message }: { message: string }) {
return (
<Card className="p-8 text-center">
<p className="text-muted-foreground">{message}</p>
<p className="text-sm text-muted-foreground mt-2">
Try creating some data via the API or check your authentication
</p>
</Card>
);
}