import React, { useState, useEffect, useRef, useCallback } from "react"; import { PriorityType, StatusType, Task } from "../../entities/Task"; import TaskActions from "./TaskActions"; import PriorityDropdown from "../Shared/PriorityDropdown"; import StatusDropdown from "../Shared/StatusDropdown"; import ConfirmDialog from "../Shared/ConfirmDialog"; import { useToast } from "../Shared/ToastContext"; import TagInput from "../Tag/TagInput"; import { Project } from "../../entities/Project"; import { Tag } from "../../entities/Tag"; interface TaskModalProps { isOpen: boolean; onClose: () => void; task: Task; onSave: (task: Task) => void; onDelete: (taskId: number) => void; projects: Project[]; onCreateProject: (name: string) => Promise; } const TaskModal: React.FC = ({ isOpen, onClose, task, onSave, onDelete, projects, onCreateProject, }) => { const [formData, setFormData] = useState(task); const [availableTags, setAvailableTags] = useState([]); const [tags, setTags] = useState( task.tags?.map((tag) => tag.name) || [] ); const [filteredProjects, setFilteredProjects] = useState(projects); const [newProjectName, setNewProjectName] = useState(""); const [isCreatingProject, setIsCreatingProject] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false); const modalRef = useRef(null); const [isClosing, setIsClosing] = useState(false); const [showConfirmDialog, setShowConfirmDialog] = useState(false); // State to control confirm dialog const { showSuccessToast, showErrorToast } = useToast(); // Use toast functions useEffect(() => { setFormData(task); setTags(task.tags?.map((tag) => tag.name) || []); }, [task]); useEffect(() => { if (isOpen) { fetch("/api/tags") .then((response) => response.json()) .then((data) => setAvailableTags(data.map((tag: Tag) => tag.name))) .catch((error) => console.error("Failed to fetch tags", error)); } }, [isOpen]); const handleChange = ( e: React.ChangeEvent< HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement > ) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value, })); }; const handleTagsChange = useCallback((newTags: string[]) => { setTags(newTags); setFormData((prev) => ({ ...prev, tags: newTags.map((name) => ({ name })), })); }, []); const handleProjectSearch = (e: React.ChangeEvent) => { const query = e.target.value.toLowerCase(); setNewProjectName(query); setDropdownOpen(true); setFilteredProjects( projects.filter((project) => project.name.toLowerCase().includes(query)) ); }; const handleProjectSelection = (project: Project) => { setFormData({ ...formData, project_id: project.id }); setNewProjectName(project.name); setDropdownOpen(false); }; const handleCreateProject = async () => { if (newProjectName.trim() !== "") { setIsCreatingProject(true); try { const newProject = await onCreateProject(newProjectName); setFormData({ ...formData, project_id: newProject.id }); setFilteredProjects([...filteredProjects, newProject]); setNewProjectName(newProject.name); setDropdownOpen(false); showSuccessToast("Project created successfully!"); } catch (error) { showErrorToast("Failed to create project."); console.error("Error creating project:", error); } finally { setIsCreatingProject(false); } } }; const handleSubmit = () => { onSave({ ...formData, tags: tags.map((tag) => ({ name: tag })) }); showSuccessToast("Task updated successfully!"); handleClose(); }; const handleDeleteClick = () => { setShowConfirmDialog(true); // Show confirmation dialog }; const handleDeleteConfirm = () => { if (formData.id) { onDelete(formData.id); showSuccessToast("Task deleted successfully!"); setShowConfirmDialog(false); handleClose(); } }; const handleClose = () => { setIsClosing(true); setTimeout(() => { onClose(); setIsClosing(false); }, 300); }; // Handler to remove tag const handleTagRemove = (tagId: string | number | undefined) => { if (tagId === undefined) return; const tagIndex = Number(tagId); if (tagIndex >= 0 && tagIndex < tags.length) { const updatedTags = tags.filter((_, index) => index !== tagIndex); setTags(updatedTags); setFormData((prev) => ({ ...prev, tags: updatedTags.map((name) => ({ name })), })); showSuccessToast("Tag removed successfully!"); } }; useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( modalRef.current && !modalRef.current.contains(event.target as Node) ) { handleClose(); } }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [isOpen]); // Handle Escape key to close modal useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { handleClose(); } }; if (isOpen) { document.addEventListener("keydown", handleKeyDown); } return () => { document.removeEventListener("keydown", handleKeyDown); }; }, [isOpen]); if (!isOpen) return null; return ( <>
{/* Task Name */}
{/* Tags */}
tag.name) || []} availableTags={availableTags} />
{/* Project */}
{dropdownOpen && newProjectName && (
{filteredProjects.length > 0 ? ( filteredProjects.map((project) => ( )) ) : (
No matching projects
)} {newProjectName && ( )}
)}
{/* Status and Priority */}
setFormData({ ...formData, status: value }) } />
setFormData({ ...formData, priority: value }) } />
{/* Note */}
{/* Task Actions */}
{showConfirmDialog && ( setShowConfirmDialog(false)} /> )} ); }; export default TaskModal;