import React, { useEffect, useState, useRef, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'; import ConfirmDialog from '../Shared/ConfirmDialog'; import { Task } from '../../entities/Task'; import { Project } from '../../entities/Project'; import { updateTask, deleteTask, fetchTaskByUid, fetchTaskNextIterations, fetchSubtasks, TaskIteration, toggleTaskCompletion, } from '../../utils/tasksService'; import { createProject } from '../../utils/projectsService'; import { fetchAttachments } from '../../utils/attachmentsService'; import { useStore } from '../../store/useStore'; import { useToast } from '../Shared/ToastContext'; import LoadingScreen from '../Shared/LoadingScreen'; import TaskTimeline from './TaskTimeline'; import { TaskDetailsHeader, TaskContentCard, TaskProjectCard, TaskTagsCard, TaskSubtasksCard, TaskRecurrenceCard, TaskDueDateCard, TaskDeferUntilCard, TaskAttachmentsCard, } from './TaskDetails/'; import { isTaskOverdueInTodayPlan, isTaskPastDue } from '../../utils/dateUtils'; const TaskDetails: React.FC = () => { const { uid } = useParams<{ uid: string }>(); const navigate = useNavigate(); const { t } = useTranslation(); const { showSuccessToast, showErrorToast } = useToast(); const projectsStore = useStore((state: any) => state.projectsStore); const tagsStore = useStore((state: any) => state.tagsStore); const tasksStore = useStore((state: any) => state.tasksStore); const task = useStore((state: any) => state.tasksStore.tasks.find((t: Task) => t.uid === uid) ); const subtasks = task?.subtasks || []; const [loading, setLoading] = useState(!task); const [error, setError] = useState(null); const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); const [taskToDelete, setTaskToDelete] = useState(null); const [timelineRefreshKey, setTimelineRefreshKey] = useState(0); const [isOverdueBubbleVisible, setIsOverdueBubbleVisible] = useState(false); const [nextIterations, setNextIterations] = useState([]); const [loadingIterations, setLoadingIterations] = useState(false); const [parentTask, setParentTask] = useState(null); const [loadingParent, setLoadingParent] = useState(false); const [pendingSubtasks, setPendingSubtasks] = useState([]); const [actionsMenuOpen, setActionsMenuOpen] = useState(false); const actionsMenuRef = useRef(null); useEffect(() => { const handleClickOutside = (e: MouseEvent) => { const target = e.target as Node; if ( actionsMenuOpen && actionsMenuRef.current && !actionsMenuRef.current.contains(target) ) { setActionsMenuOpen(false); } if (isOverdueBubbleVisible) { const clickedOverdueToggle = typeof e.composedPath === 'function' ? e .composedPath() .some( (node) => node instanceof HTMLElement && node.hasAttribute('data-overdue-toggle') ) : target instanceof HTMLElement && !!target.closest('[data-overdue-toggle]'); if (!clickedOverdueToggle) { setIsOverdueBubbleVisible(false); } } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [actionsMenuOpen, isOverdueBubbleVisible]); const [isEditingDueDate, setIsEditingDueDate] = useState(false); const [editedDueDate, setEditedDueDate] = useState( task?.due_date || '' ); const [isEditingDeferUntil, setIsEditingDeferUntil] = useState(false); const [editedDeferUntil, setEditedDeferUntil] = useState( task?.defer_until || '' ); const [isEditingRecurrence, setIsEditingRecurrence] = useState(false); const [recurrenceForm, setRecurrenceForm] = useState({ recurrence_type: task?.recurrence_type || 'none', recurrence_interval: task?.recurrence_interval || 1, recurrence_end_date: task?.recurrence_end_date || '', recurrence_weekday: task?.recurrence_weekday || null, recurrence_weekdays: task?.recurrence_weekdays || [], recurrence_month_day: task?.recurrence_month_day || null, recurrence_week_of_month: task?.recurrence_week_of_month || null, completion_based: task?.completion_based || false, }); const [activePill, setActivePill] = useState('overview'); const [attachmentCount, setAttachmentCount] = useState(0); const [hasLoadedSubtasks, setHasLoadedSubtasks] = useState(false); useEffect(() => { setEditedDueDate(task?.due_date || ''); }, [task?.due_date]); useEffect(() => { setRecurrenceForm({ recurrence_type: task?.recurrence_type || 'none', recurrence_interval: task?.recurrence_interval || 1, recurrence_end_date: task?.recurrence_end_date || '', recurrence_weekday: task?.recurrence_weekday || null, recurrence_weekdays: task?.recurrence_weekdays || [], recurrence_month_day: task?.recurrence_month_day || null, recurrence_week_of_month: task?.recurrence_week_of_month || null, completion_based: task?.completion_based || false, }); }, [ task?.recurrence_type, task?.recurrence_interval, task?.recurrence_end_date, task?.recurrence_weekday, task?.recurrence_weekdays, task?.recurrence_month_day, task?.recurrence_week_of_month, task?.completion_based, ]); useEffect(() => { if (!tagsStore.hasLoaded && !tagsStore.isLoading) { tagsStore.loadTags(); } }, [tagsStore]); const handleStartRecurrenceEdit = () => { setRecurrenceForm({ recurrence_type: task?.recurrence_type || 'none', recurrence_interval: task?.recurrence_interval || 1, recurrence_end_date: task?.recurrence_end_date || '', recurrence_weekday: task?.recurrence_weekday || null, recurrence_weekdays: task?.recurrence_weekdays || [], recurrence_month_day: task?.recurrence_month_day || null, recurrence_week_of_month: task?.recurrence_week_of_month || null, completion_based: task?.completion_based || false, }); setIsEditingRecurrence(true); }; const isOverdue = task ? isTaskOverdueInTodayPlan(task) : false; const isPastDue = task ? isTaskPastDue(task) : false; useEffect(() => { if (!isOverdue) { setIsOverdueBubbleVisible(false); } }, [isOverdue]); const handleOverdueIconClick = () => { if (!isOverdue) { return; } setIsOverdueBubbleVisible((prev) => !prev); }; const handleDismissOverdueAlert = () => { setIsOverdueBubbleVisible(false); }; const handleRecurrenceChange = (field: string, value: any) => { setRecurrenceForm((prev) => { const updated = { ...prev, [field]: value }; if ( field === 'recurrence_type' && value === 'monthly' && !prev.recurrence_month_day ) { updated.recurrence_month_day = new Date().getDate(); } return updated; }); }; const handleSaveRecurrence = async () => { if (!task?.uid) { setIsEditingRecurrence(false); return; } try { const recurrencePayload: Partial = { recurrence_type: recurrenceForm.recurrence_type, recurrence_interval: recurrenceForm.recurrence_interval || 1, recurrence_end_date: recurrenceForm.recurrence_end_date || null, recurrence_weekday: recurrenceForm.recurrence_type === 'weekly' || recurrenceForm.recurrence_type === 'monthly_weekday' ? recurrenceForm.recurrence_weekday || null : null, recurrence_weekdays: recurrenceForm.recurrence_type === 'weekly' ? recurrenceForm.recurrence_weekdays || [] : null, recurrence_month_day: recurrenceForm.recurrence_type === 'monthly' ? recurrenceForm.recurrence_month_day || null : null, recurrence_week_of_month: recurrenceForm.recurrence_type === 'monthly_weekday' ? recurrenceForm.recurrence_week_of_month || null : null, completion_based: recurrenceForm.completion_based, }; await updateTask(task.uid, { ...task, ...recurrencePayload }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.recurrenceUpdated', 'Recurrence updated successfully') ); setIsEditingRecurrence(false); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating recurrence:', error); showErrorToast( t('task.recurrenceUpdateError', 'Failed to update recurrence') ); setIsEditingRecurrence(false); } }; const handleCancelRecurrenceEdit = () => { setIsEditingRecurrence(false); setRecurrenceForm({ recurrence_type: task?.recurrence_type || 'none', recurrence_interval: task?.recurrence_interval || 1, recurrence_end_date: task?.recurrence_end_date || '', recurrence_weekday: task?.recurrence_weekday || null, recurrence_weekdays: task?.recurrence_weekdays || [], recurrence_month_day: task?.recurrence_month_day || null, recurrence_week_of_month: task?.recurrence_week_of_month || null, completion_based: task?.completion_based || false, }); }; const handleStartDueDateEdit = () => { setEditedDueDate(task?.due_date || ''); setIsEditingDueDate(true); }; const handleSaveDueDate = async () => { if (!task?.uid) { setIsEditingDueDate(false); setEditedDueDate(task?.due_date || ''); return; } if ((editedDueDate || '') === (task.due_date || '')) { setIsEditingDueDate(false); return; } if (task.defer_until && editedDueDate) { const deferDate = new Date(task.defer_until); const dueDate = new Date(editedDueDate); if (!isNaN(deferDate.getTime()) && !isNaN(dueDate.getTime())) { if (deferDate > dueDate) { showErrorToast( t( 'task.dueDateBeforeDeferError', 'Due date cannot be before the defer until date' ) ); return; } } } if (editedDueDate) { const dueDate = new Date(editedDueDate); const today = new Date(); today.setHours(0, 0, 0, 0); dueDate.setHours(0, 0, 0, 0); if (!isNaN(dueDate.getTime()) && dueDate < today) { showErrorToast( t( 'task.dueDateInPastWarning', 'Warning: You are setting a due date in the past' ) ); } } try { await updateTask(task.uid, { ...task, due_date: editedDueDate || null, }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.dueDateUpdated', 'Due date updated successfully') ); setIsEditingDueDate(false); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating due date:', error); showErrorToast( t('task.dueDateUpdateError', 'Failed to update due date') ); setEditedDueDate(task.due_date || ''); setIsEditingDueDate(false); } }; const handleCancelDueDateEdit = () => { setIsEditingDueDate(false); setEditedDueDate(task?.due_date || ''); }; const handleStartDeferUntilEdit = () => { setEditedDeferUntil(task?.defer_until || ''); setIsEditingDeferUntil(true); }; const handleSaveDeferUntil = async () => { if (!task?.uid) { setIsEditingDeferUntil(false); setEditedDeferUntil(task?.defer_until || ''); return; } if ((editedDeferUntil || '') === (task.defer_until || '')) { setIsEditingDeferUntil(false); return; } if (editedDeferUntil && task.due_date) { const deferDate = new Date(editedDeferUntil); const dueDate = new Date(task.due_date); if (!isNaN(deferDate.getTime()) && !isNaN(dueDate.getTime())) { if (deferDate > dueDate) { showErrorToast( t( 'task.deferAfterDueError', 'Defer until date cannot be after the due date' ) ); return; } } } try { await updateTask(task.uid, { defer_until: editedDeferUntil || null, }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.deferUntilUpdated', 'Defer until successfully updated') ); setIsEditingDeferUntil(false); setTimelineRefreshKey((prev) => prev + 1); } catch (error: any) { console.error('Error updating defer until:', error); showErrorToast( error?.message || t( 'task.deferUntilUpdateError', 'Failed to update defer until' ) ); setEditedDeferUntil(task?.defer_until || ''); setIsEditingDeferUntil(false); } }; const handleCancelDeferUntilEdit = () => { setIsEditingDeferUntil(false); setEditedDeferUntil(task?.defer_until || ''); }; useEffect(() => { const fetchTaskData = async () => { if (!uid) { setError('No task uid provided'); setLoading(false); return; } if (!task) { try { setLoading(true); const fetchedTask = await fetchTaskByUid(uid); tasksStore.setTasks([...tasksStore.tasks, fetchedTask]); } catch (fetchError) { setError('Task not found'); console.error('Error fetching task:', fetchError); } finally { setLoading(false); } } }; fetchTaskData(); }, [uid, task, tasksStore]); useEffect(() => { const loadAttachmentCount = async () => { if (task?.uid) { try { const attachments = await fetchAttachments(task.uid); setAttachmentCount(attachments.length); } catch (error) { console.error('Error loading attachment count:', error); } } }; loadAttachmentCount(); }, [task?.uid]); useEffect(() => { setHasLoadedSubtasks(false); }, [uid]); useEffect(() => { const loadSubtasks = async () => { const subtasksAlreadyLoaded = task?.subtasks && task.subtasks.length > 0; if ( activePill === 'subtasks' && task?.uid && !hasLoadedSubtasks && !subtasksAlreadyLoaded ) { try { const fetchedSubtasks = await fetchSubtasks(task.uid); setHasLoadedSubtasks(true); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === task.uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = { ...task, subtasks: fetchedSubtasks, }; tasksStore.setTasks(updatedTasks); } } catch (error) { console.error('Error loading subtasks:', error); setHasLoadedSubtasks(true); } } }; loadSubtasks(); }, [activePill, task?.uid, task?.subtasks, hasLoadedSubtasks, tasksStore]); useEffect(() => { setPendingSubtasks(subtasks); }, [subtasks]); useEffect(() => { const loadNextIterations = async () => { if ( task?.id && task.recurrence_type && task.recurrence_type !== 'none' ) { try { setLoadingIterations(true); const iterations = await fetchTaskNextIterations( task.uid! ); setNextIterations(iterations); } catch (error) { console.error('Error loading next iterations:', error); setNextIterations([]); } finally { setLoadingIterations(false); } } else if ( task?.recurring_parent_id && parentTask?.uid && parentTask.recurrence_type && parentTask.recurrence_type !== 'none' ) { try { setLoadingIterations(true); const iterations = await fetchTaskNextIterations( parentTask.uid ); setNextIterations(iterations); } catch (error) { console.error( 'Error loading next iterations for child task:', error ); setNextIterations([]); } finally { setLoadingIterations(false); } } else { setNextIterations([]); } }; loadNextIterations(); }, [ task?.id, task?.recurrence_type, task?.due_date, task?.recurring_parent_id, parentTask?.id, parentTask?.recurrence_type, ]); useEffect(() => { const loadParentTask = async () => { if (task?.recurring_parent_uid) { try { setLoadingParent(true); const parent = await fetchTaskByUid( task.recurring_parent_uid ); setParentTask(parent); } catch (error) { console.error('Error fetching parent task:', error); setParentTask(null); } finally { setLoadingParent(false); } } }; loadParentTask(); }, [task?.recurring_parent_uid]); const handleSaveSubtasks = async (subtasksToSave: Task[]) => { if (!task?.uid) { return; } const hasChanges = subtasksToSave.length !== subtasks.length || subtasksToSave.some( (ps, i) => !subtasks[i] || ps.name !== subtasks[i].name || ps.status !== subtasks[i].status || (ps as any)._isNew || (ps as any)._isEdited || (ps as any)._statusChanged ); if (!hasChanges) { return; } try { await updateTask(task.uid, { ...task, subtasks: subtasksToSave }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating subtasks:', error); showErrorToast( t('task.subtasksUpdateError', 'Failed to update subtasks') ); setPendingSubtasks([...subtasks]); } }; const handleProjectSelection = async (project: Project) => { if (!task?.uid) return; try { await updateTask(task.uid, { ...task, project_id: project.id }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.projectUpdated', 'Project updated successfully') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating project:', error); showErrorToast( t('task.projectUpdateError', 'Failed to update project') ); } }; const handleClearProject = async () => { if (!task?.uid) return; try { await updateTask(task.uid, { ...task, project_id: null }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.projectCleared', 'Project cleared successfully') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error clearing project:', error); showErrorToast( t('task.projectClearError', 'Failed to clear project') ); } }; const refreshRecurringSetup = useCallback( async (latestTask?: Task | null) => { if (!latestTask) { setNextIterations([]); return; } const isTemplateTask = latestTask.recurrence_type && latestTask.recurrence_type !== 'none' && !latestTask.recurring_parent_id; const canUseParentIterations = !!latestTask.recurring_parent_id && !!parentTask?.id && parentTask?.recurrence_type && parentTask.recurrence_type !== 'none'; if (!isTemplateTask && !canUseParentIterations) { setNextIterations([]); return; } try { setLoadingIterations(true); if (isTemplateTask) { const iterations = await fetchTaskNextIterations( latestTask.uid! ); setNextIterations(iterations); } else if (canUseParentIterations && parentTask?.uid) { const iterations = await fetchTaskNextIterations( parentTask.uid ); setNextIterations(iterations); } } catch (error) { console.error('Error refreshing recurring setup:', error); setNextIterations([]); } finally { setLoadingIterations(false); } }, [parentTask?.id, parentTask?.recurrence_type] ); const handleCompletionToggle = async () => { if (!task?.uid) { return; } try { const updatedTaskResponse = await toggleTaskCompletion( task.uid, task ); const mergedTask = { ...task, ...updatedTaskResponse, subtasks: updatedTaskResponse.subtasks || task.subtasks || [], }; if (uid) { const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = mergedTask; tasksStore.setTasks(updatedTasks); } } await refreshRecurringSetup(mergedTask); setTimelineRefreshKey((prev) => prev + 1); showSuccessToast( t('task.statusUpdated', 'Status updated successfully') ); } catch (error) { console.error('Error toggling task completion:', error); showErrorToast( t('task.statusUpdateError', 'Failed to update status') ); } }; const handleStatusUpdate = async (newStatus: number) => { if (!task?.uid) return; try { await updateTask(task.uid, { ...task, status: newStatus, }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.statusUpdated', 'Status updated successfully') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating status:', error); showErrorToast( t('task.statusUpdateError', 'Failed to update status') ); } }; const handleDeleteClick = () => { if (task) { setTaskToDelete(task); setIsConfirmDialogOpen(true); } }; const handleDeleteConfirm = async () => { if (taskToDelete?.uid) { try { await deleteTask(taskToDelete.uid); showSuccessToast( t('task.deleteSuccess', 'Task deleted successfully') ); navigate('/today'); } catch (error) { console.error('Error deleting task:', error); showErrorToast(t('task.deleteError', 'Failed to delete task')); } } setIsConfirmDialogOpen(false); setTaskToDelete(null); }; const getProjectLink = (project: Project) => { if (project.uid) { const slug = project.name .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); return `/project/${project.uid}-${slug}`; } return `/project/${project.id}`; }; const getTagLink = (tag: any) => { if (tag.uid) { const slug = tag.name .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); return `/tag/${tag.uid}-${slug}`; } return `/tag/${encodeURIComponent(tag.name)}`; }; const handleTitleUpdate = async (newTitle: string) => { if (!task?.uid || !newTitle.trim()) { return; } if (newTitle.trim() === task.name) { return; } try { await updateTask(task.uid, { ...task, name: newTitle.trim() }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.titleUpdated', 'Task title updated successfully') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating task title:', error); showErrorToast( t('task.titleUpdateError', 'Failed to update task title') ); throw error; } }; const handleContentUpdate = async (newContent: string) => { if (!task?.uid) { return; } const trimmedContent = newContent.trim(); if (trimmedContent === (task.note || '').trim()) { return; } try { await updateTask(task.uid, { ...task, note: trimmedContent }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.contentUpdated', 'Task content updated successfully') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating task content:', error); showErrorToast( t('task.contentUpdateError', 'Failed to update task content') ); throw error; } }; const handleProjectCreateInlineWrapper = async (name: string) => { if (!task?.uid || !name.trim()) return; try { const newProject = await createProject({ name }); projectsStore.setProjects([...projectsStore.projects, newProject]); await updateTask(task.uid, { ...task, project_id: newProject.id }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('project.createdAndAssigned', 'Project created and assigned') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error creating project:', error); showErrorToast( t('project.createError', 'Failed to create project') ); throw error; } }; const handleTagsUpdate = async (tags: string[]) => { if (!task?.uid) { return; } const currentTags = task.tags?.map((tag: any) => tag.name) || []; if ( tags.length === currentTags.length && tags.every((tag, idx) => tag === currentTags[idx]) ) { return; } try { await updateTask(task.uid, { ...task, tags: tags.map((name) => ({ name })), }); if (uid) { const updatedTask = await fetchTaskByUid(uid); const existingIndex = tasksStore.tasks.findIndex( (t: Task) => t.uid === uid ); if (existingIndex >= 0) { const updatedTasks = [...tasksStore.tasks]; updatedTasks[existingIndex] = updatedTask; tasksStore.setTasks(updatedTasks); } } showSuccessToast( t('task.tagsUpdated', 'Tags updated successfully') ); setTimelineRefreshKey((prev) => prev + 1); } catch (error) { console.error('Error updating tags:', error); showErrorToast(t('task.tagsUpdateError', 'Failed to update tags')); throw error; } }; const handlePriorityUpdate = async (priority: any) => { if (!task?.uid) return; try { await updateTask(task.uid, { ...task, priority: priority, }); const updatedTask = await fetchTaskByUid(uid!); tasksStore.updateTaskInStore(updatedTask); setTimelineRefreshKey((prev) => prev + 1); showSuccessToast( t('task.priorityUpdated', 'Priority updated successfully') ); } catch (error) { console.error('Error updating priority:', error); showErrorToast( t('task.priorityUpdateError', 'Failed to update priority') ); throw error; } }; if (loading) { return ; } if (error || !task) { return (

{error || t('task.notFound', 'Task Not Found')}

{t( 'task.notFoundDescription', 'The task you are looking for does not exist or has been deleted.' )}

); } return (
{activePill === 'overview' && (
tagsStore.loadTags()} getTagLink={getTagLink} />
)} {activePill === 'recurrence' && (
)} {activePill === 'subtasks' && (
)} {activePill === 'attachments' && (
)} {activePill === 'activity' && (
)}
{isConfirmDialogOpen && taskToDelete && ( { setIsConfirmDialogOpen(false); setTaskToDelete(null); }} /> )}
); }; export default TaskDetails;