Add trace files.
This commit is contained in:
96
docs/plans/sprint_1_story_2_task_3.md
Normal file
96
docs/plans/sprint_1_story_2_task_3.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Task 7: Implement Epic/Story/Task Forms
|
||||
|
||||
**Task ID**: TASK-007 | **Story**: [STORY-002](sprint_1_story_2.md) | **Sprint**: [Sprint 1](sprint_1.md)
|
||||
**Estimated Hours**: 5h | **Assignee**: Frontend Developer 2 | **Priority**: P0 | **Status**: Not Started
|
||||
|
||||
## Task Description
|
||||
Build React forms for creating/editing Epic/Story/Task with validation, parent selection, and error handling.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Example: EpicForm.tsx
|
||||
```typescript
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { useCreateEpic, useUpdateEpic } from '../hooks/useEpics';
|
||||
|
||||
const epicSchema = z.object({
|
||||
title: z.string().min(3, 'Title must be at least 3 characters'),
|
||||
description: z.string().optional(),
|
||||
projectId: z.string().uuid('Invalid project ID'),
|
||||
priority: z.enum(['Low', 'Medium', 'High', 'Critical']),
|
||||
status: z.enum(['Backlog', 'Todo', 'InProgress', 'Done'])
|
||||
});
|
||||
|
||||
type EpicFormData = z.infer<typeof epicSchema>;
|
||||
|
||||
export const EpicForm: React.FC<{ epic?: Epic, onSuccess: () => void }> = ({ epic, onSuccess }) => {
|
||||
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<EpicFormData>({
|
||||
resolver: zodResolver(epicSchema),
|
||||
defaultValues: epic || {}
|
||||
});
|
||||
|
||||
const createEpic = useCreateEpic();
|
||||
const updateEpic = useUpdateEpic();
|
||||
|
||||
const onSubmit = async (data: EpicFormData) => {
|
||||
try {
|
||||
if (epic) {
|
||||
await updateEpic.mutateAsync({ id: epic.id, dto: data });
|
||||
} else {
|
||||
await createEpic.mutateAsync(data);
|
||||
}
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="title">Title *</label>
|
||||
<input id="title" {...register('title')} className="form-input" />
|
||||
{errors.title && <span className="text-red-500">{errors.title.message}</span>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="description">Description</label>
|
||||
<textarea id="description" {...register('description')} className="form-textarea" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="priority">Priority *</label>
|
||||
<select id="priority" {...register('priority')} className="form-select">
|
||||
<option value="Low">Low</option>
|
||||
<option value="Medium">Medium</option>
|
||||
<option value="High">High</option>
|
||||
<option value="Critical">Critical</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" disabled={isSubmitting} className="btn-primary">
|
||||
{isSubmitting ? 'Saving...' : (epic ? 'Update Epic' : 'Create Epic')}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] EpicForm component with validation
|
||||
- [ ] StoryForm with parent epic selection dropdown
|
||||
- [ ] TaskForm with parent story selection dropdown
|
||||
- [ ] Zod schemas for all forms
|
||||
- [ ] Loading states during submission
|
||||
- [ ] Error messages displayed
|
||||
|
||||
## Deliverables
|
||||
1. EpicForm.tsx
|
||||
2. StoryForm.tsx (with parent epic selector)
|
||||
3. TaskForm.tsx (with parent story selector)
|
||||
4. Validation schemas
|
||||
5. Unit tests (15+ tests)
|
||||
|
||||
**Status**: Not Started | **Created**: 2025-11-04
|
||||
Reference in New Issue
Block a user