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>
This commit is contained in:
Yaojia Wang
2025-11-05 13:30:48 +01:00
parent 3fa43c5542
commit 04ba00d108
7 changed files with 21 additions and 11 deletions

View File

@@ -26,9 +26,10 @@ import { useCreateEpic, useUpdateEpic } from '@/lib/hooks/use-epics';
import type { Epic, WorkItemPriority } from '@/types/project';
import { toast } from 'sonner';
import { Loader2 } from 'lucide-react';
import { useAuthStore } from '@/stores/authStore';
const epicSchema = z.object({
title: z
name: z
.string()
.min(1, 'Title is required')
.max(200, 'Title must be less than 200 characters'),
@@ -57,11 +58,12 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps
const isEditing = !!epic;
const createEpic = useCreateEpic();
const updateEpic = useUpdateEpic();
const user = useAuthStore((state) => state.user);
const form = useForm<EpicFormValues>({
resolver: zodResolver(epicSchema),
defaultValues: {
title: epic?.title || '',
name: epic?.name || '', // Fixed: use 'name' instead of 'title'
description: epic?.description || '',
priority: epic?.priority || 'Medium',
estimatedHours: epic?.estimatedHours || ('' as any),
@@ -70,6 +72,11 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps
async function onSubmit(data: EpicFormValues) {
try {
if (!user?.id) {
toast.error('User not authenticated');
return;
}
const payload = {
...data,
estimatedHours: data.estimatedHours || undefined,
@@ -83,6 +90,7 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps
} else {
await createEpic.mutateAsync({
projectId,
createdBy: user.id,
...payload,
});
}
@@ -107,7 +115,7 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="title"
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Epic Title *</FormLabel>

View File

@@ -102,7 +102,7 @@ function EpicNode({ epic, onEpicClick, onStoryClick, onTaskClick }: EpicNodeProp
onEpicClick?.(epic);
}}
>
{epic.title}
{epic.name}
</span>
<StatusBadge status={epic.status} />
<PriorityBadge priority={epic.priority} />

View File

@@ -144,7 +144,7 @@ export function StoryForm({
) : (
epics.map((epic) => (
<SelectItem key={epic.id} value={epic.id}>
{epic.title}
{epic.name}
</SelectItem>
))
)}