EpicFormModal.tsxā¢4.13 kB
import { useState, useEffect } from 'react';
import { X } from 'lucide-react';
interface Epic {
id?: number;
project_id: number;
title: string;
description: string;
status: 'todo' | 'in_progress' | 'review' | 'done' | 'blocked';
}
interface EpicFormModalProps {
isOpen: boolean;
onClose: () => void;
onSave: () => void;
epic?: Epic | null;
projectId: number;
}
export default function EpicFormModal({ isOpen, onClose, onSave, epic, projectId }: EpicFormModalProps) {
const [formData, setFormData] = useState<Epic>({
project_id: projectId,
title: '',
description: '',
status: 'todo',
});
useEffect(() => {
if (epic) {
setFormData(epic);
} else {
setFormData({
project_id: projectId,
title: '',
description: '',
status: 'todo',
});
}
}, [epic, projectId]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const url = epic
? `/api/epics/${epic.id}`
: '/api/epics';
const method = epic ? 'PATCH' : 'POST';
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
if (response.ok) {
onSave();
onClose();
}
} catch (error) {
console.error('Failed to save epic:', error);
}
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4">
<div className="flex items-center justify-between p-6 border-b">
<h2 className="text-xl font-semibold">{epic ? 'Edit Epic' : 'Create Epic'}</h2>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
<X size={24} />
</button>
</div>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Title</label>
<input
type="text"
value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Description</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
rows={4}
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select
value={formData.status}
onChange={(e) => setFormData({ ...formData, status: e.target.value as Epic['status'] })}
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
>
<option value="todo">To Do</option>
<option value="in_progress">In Progress</option>
<option value="review">Review</option>
<option value="done">Done</option>
<option value="blocked">Blocked</option>
</select>
</div>
<div className="flex gap-3 pt-4">
<button
type="submit"
className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium"
>
{epic ? 'Update' : 'Create'}
</button>
<button
type="button"
onClick={onClose}
className="flex-1 px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 font-medium"
>
Cancel
</button>
</div>
</form>
</div>
</div>
);
}