import React, { useState, useRef } from 'react'; import { PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; import { useTranslation } from 'react-i18next'; import { Task } from '../../../entities/Task'; import TaskPriorityIcon from '../../Shared/Icons/TaskPriorityIcon'; import { toggleTaskCompletion } from '../../../utils/tasksService'; interface TaskSubtasksSectionProps { parentTaskId: number; subtasks: Task[]; onSubtasksChange: (subtasks: Task[]) => void; onSubtaskUpdate?: (subtask: Task) => Promise; onSave?: (subtasks: Task[]) => void; } const TaskSubtasksSection: React.FC = ({ parentTaskId, subtasks, onSubtasksChange, onSubtaskUpdate, onSave, }) => { const [newSubtaskName, setNewSubtaskName] = useState(''); const [isLoading] = useState(false); const [editingIndex, setEditingIndex] = useState(null); const [editingName, setEditingName] = useState(''); const { t } = useTranslation(); const subtasksSectionRef = useRef(null); const addInputRef = useRef(null); const scrollToBottom = () => { setTimeout(() => { const modalScrollContainer = document.querySelector( '.absolute.inset-0.overflow-y-auto' ); if (modalScrollContainer) { modalScrollContainer.scrollTo({ top: modalScrollContainer.scrollHeight, behavior: 'smooth', }); } }, 100); }; const handleCreateSubtask = () => { if (!newSubtaskName.trim()) return; const newSubtask: Task = { name: newSubtaskName.trim(), status: 'not_started', priority: 'low', today: false, parent_task_id: parentTaskId, isNew: true, _isNew: true, completed_at: null, } as Task; const updatedSubtasks = [...subtasks, newSubtask]; onSubtasksChange(updatedSubtasks); setNewSubtaskName(''); scrollToBottom(); onSave?.(updatedSubtasks); }; const handleDeleteSubtask = (index: number) => { const updatedSubtasks = subtasks.filter((_, i) => i !== index); onSubtasksChange(updatedSubtasks); onSave?.(updatedSubtasks); }; const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); handleCreateSubtask(); } }; const handleEditSubtask = (index: number) => { setEditingIndex(index); setEditingName(subtasks[index].name); }; const handleSaveEdit = () => { if (!editingName.trim() || editingIndex === null) return; const updatedSubtasks = subtasks.map((subtask, index) => { if (index === editingIndex) { const isNameChanged = subtask.name !== editingName.trim(); const isNew = (subtask as any)._isNew || (subtask as any).isNew || false; const isEdited = !isNew && isNameChanged; return { ...subtask, name: editingName.trim(), isNew: isNew, isEdited: isEdited, _isNew: isNew, _isEdited: isEdited, }; } return subtask; }); onSubtasksChange(updatedSubtasks); setEditingIndex(null); setEditingName(''); onSave?.(updatedSubtasks); }; const handleCancelEdit = () => { setEditingIndex(null); setEditingName(''); }; const handleToggleNewSubtaskCompletion = (index: number) => { const updatedSubtasks = subtasks.map((subtask, i) => { if (i === index) { const isDone = subtask.status === 'done' || subtask.status === 2; const newStatus = isDone ? ('not_started' as const) : ('done' as const); const hasId = subtask.id && !((subtask as any)._isNew || (subtask as any).isNew); return { ...subtask, status: newStatus, completed_at: isDone ? null : new Date().toISOString(), _statusChanged: hasId, }; } return subtask; }); onSubtasksChange(updatedSubtasks); }; const handleToggleSubtaskCompletion = async (subtask: Task, index: number) => { const isPersisted = subtask.id && subtask.uid && !((subtask as any)._isNew || (subtask as any).isNew); if (isPersisted) { try { const updatedSubtask = await toggleTaskCompletion(subtask.uid!); if (onSubtaskUpdate) { await onSubtaskUpdate(updatedSubtask); } else { const updatedSubtasks = subtasks.map((s, i) => i === index ? updatedSubtask : s ); onSubtasksChange(updatedSubtasks); } } catch (error) { console.error('Error toggling subtask completion:', error); } } else { handleToggleNewSubtaskCompletion(index); } }; return (
{isLoading ? (
{t('loading.subtasks', 'Loading subtasks...')}
) : subtasks.length > 0 ? (
{subtasks.map((subtask, index) => (
{editingIndex === index ? (
handleToggleSubtaskCompletion(subtask, index)} />
setEditingName(e.target.value) } onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleSaveEdit(); } else if (e.key === 'Escape') { handleCancelEdit(); } }} onBlur={handleSaveEdit} className="flex-1 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:text-white overflow-hidden" autoFocus />
) : (
handleToggleSubtaskCompletion(subtask, index)} />
handleEditSubtask(index) } title={t( 'actions.clickToEdit', 'Click to edit' )} > {subtask.name}
)}
))}
) : (
{t('subtasks.noSubtasks', 'No subtasks yet')}
)}
setNewSubtaskName(e.target.value)} onKeyDown={handleKeyPress} placeholder={t('subtasks.placeholder', 'Add a subtask...')} className="flex-1 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white overflow-hidden" />
); }; export default TaskSubtasksSection;