'use client'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; import { Button } from '@/components/ui/button'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useCreateEpic, useUpdateEpic } from '@/lib/hooks/use-epics'; import type { Epic, WorkItemPriority } from '@/types/project'; import { toast } from 'sonner'; import { Loader2 } from 'lucide-react'; const epicSchema = z.object({ title: z .string() .min(1, 'Title is required') .max(200, 'Title must be less than 200 characters'), description: z .string() .max(2000, 'Description must be less than 2000 characters') .optional(), priority: z.enum(['Low', 'Medium', 'High', 'Critical']), estimatedHours: z .number() .min(0, 'Estimated hours must be positive') .optional() .or(z.literal('')), }); type EpicFormValues = z.infer; interface EpicFormProps { projectId: string; epic?: Epic; onSuccess?: () => void; onCancel?: () => void; } export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps) { const isEditing = !!epic; const createEpic = useCreateEpic(); const updateEpic = useUpdateEpic(); const form = useForm({ resolver: zodResolver(epicSchema), defaultValues: { title: epic?.title || '', description: epic?.description || '', priority: epic?.priority || 'Medium', estimatedHours: epic?.estimatedHours || ('' as any), }, }); async function onSubmit(data: EpicFormValues) { try { const payload = { ...data, estimatedHours: data.estimatedHours || undefined, }; if (isEditing) { await updateEpic.mutateAsync({ id: epic.id, data: payload, }); } else { await createEpic.mutateAsync({ projectId, ...payload, }); } onSuccess?.(); } catch (error) { const message = error instanceof Error ? error.message : 'Operation failed'; toast.error(message); } } const isLoading = createEpic.isPending || updateEpic.isPending; const priorityOptions: Array<{ value: WorkItemPriority; label: string; color: string }> = [ { value: 'Low', label: 'Low', color: 'text-blue-600' }, { value: 'Medium', label: 'Medium', color: 'text-yellow-600' }, { value: 'High', label: 'High', color: 'text-orange-600' }, { value: 'Critical', label: 'Critical', color: 'text-red-600' }, ]; return (
( Epic Title * A concise title describing this epic )} /> ( Description