import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { CalendarDaysIcon, CalendarIcon, PlayIcon, ArrowPathIcon, ArrowRightIcon, Squares2X2Icon, } from '@heroicons/react/24/outline'; import { TagIcon, FolderIcon } from '@heroicons/react/24/solid'; import { useTranslation } from 'react-i18next'; import TaskPriorityIcon from './TaskPriorityIcon'; import { Project } from '../../entities/Project'; import { Task, StatusType } from '../../entities/Task'; import { fetchSubtasks } from '../../utils/tasksService'; interface TaskHeaderProps { task: Task; project?: Project; onTaskClick: (e: React.MouseEvent) => void; onToggleCompletion?: () => void; hideProjectName?: boolean; onToggleToday?: (taskId: number) => Promise; onTaskUpdate?: (task: Task) => Promise; isOverdue?: boolean; // Props for subtasks functionality showSubtasks?: boolean; hasSubtasks?: boolean; onSubtasksToggle?: (e: React.MouseEvent) => void; } const TaskHeader: React.FC = ({ task, project, onTaskClick, onToggleCompletion, hideProjectName = false, onToggleToday, onTaskUpdate, isOverdue = false, // Props for subtasks functionality showSubtasks, hasSubtasks, onSubtasksToggle, }) => { const { t } = useTranslation(); const formatDueDate = (dueDate: string) => { const today = new Date().toISOString().split('T')[0]; const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000) .toISOString() .split('T')[0]; const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000) .toISOString() .split('T')[0]; if (dueDate === today) return t('dateIndicators.today', 'TODAY'); if (dueDate === tomorrow) return t('dateIndicators.tomorrow', 'TOMORROW'); if (dueDate === yesterday) return t('dateIndicators.yesterday', 'YESTERDAY'); return new Date(dueDate).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', }); }; const formatRecurrence = (recurrenceType: string) => { switch (recurrenceType) { case 'daily': return t('recurrence.daily', 'Daily'); case 'weekly': return t('recurrence.weekly', 'Weekly'); case 'monthly': return t('recurrence.monthly', 'Monthly'); case 'monthly_weekday': return t('recurrence.monthlyWeekday', 'Monthly'); case 'monthly_last_day': return t('recurrence.monthlyLastDay', 'Monthly'); default: return t('recurrence.recurring', 'Recurring'); } }; const handleTodayToggle = async (e: React.MouseEvent) => { e.stopPropagation(); // Prevent opening task modal if (onToggleToday && task.id) { try { await onToggleToday(task.id); } catch (error) { console.error('Failed to toggle today status:', error); } } }; const handlePlayToggle = async (e: React.MouseEvent) => { e.stopPropagation(); // Prevent opening task modal if ( task.id && (task.status === 'not_started' || task.status === 'in_progress' || task.status === 0 || task.status === 1) && onTaskUpdate ) { try { const isCurrentlyInProgress = task.status === 'in_progress' || task.status === 1; const updatedTask = { ...task, status: (isCurrentlyInProgress ? 'not_started' : 'in_progress') as StatusType, // Automatically add to today plan when setting to in_progress today: isCurrentlyInProgress ? task.today : true, }; await onTaskUpdate(updatedTask); } catch (error) { console.error('Failed to toggle in progress status:', error); } } }; // Check if task has metadata (project, tags, due_date, recurrence_type) const hasMetadata = ( (project && !hideProjectName) || (task.tags && task.tags.length > 0) || task.due_date || (task.recurrence_type && task.recurrence_type !== 'none') ); return (
{/* Full view (md and larger) */}
{task.name} {isOverdue && ( )}
{/* Project, tags, due date, and recurrence in same row, with spacing when they exist */}
{project && !hideProjectName && (
e.stopPropagation()} > {project.name}
)} {project && !hideProjectName && task.tags && task.tags.length > 0 && ( )} {task.tags && task.tags.length > 0 && (
{task.tags.map((tag, index) => ( {tag.name} {index < task.tags!.length - 1 && ', '} ))}
)} {((project && !hideProjectName) || (task.tags && task.tags.length > 0)) && task.due_date && ( )} {task.due_date && (
{formatDueDate(task.due_date)}
)} {((project && !hideProjectName) || (task.tags && task.tags.length > 0) || task.due_date) && task.recurrence_type && task.recurrence_type !== 'none' && ( )} {task.recurrence_type && task.recurrence_type !== 'none' && (
{formatRecurrence( task.recurrence_type )}
)}
{/* Today Plan Controls */} {onToggleToday && ( )} {/* Play/In Progress Controls */} {(task.status === 'not_started' || task.status === 'in_progress' || task.status === 0 || task.status === 1) && ( )} {/* Show Subtasks Controls - Hide for completed tasks */} {hasSubtasks && !(task.status === 'done' || task.status === 2) && ( )}
{/* Mobile view (below md breakpoint) */}
{/* Priority Icon - Centered vertically with entire card */}
{/* Task content - 65% width */}
{/* Task Title */}
{task.name} {isOverdue && ( )}
{/* Project, tags, due date, and recurrence */}
{project && !hideProjectName && (
e.stopPropagation()} > {project.name}
)} {task.tags && task.tags.length > 0 && (
{task.tags.map((tag, index) => ( {tag.name} {index < task.tags!.length - 1 && ', '} ))}
)} {task.due_date && (
{formatDueDate(task.due_date)}
)} {task.recurrence_type && task.recurrence_type !== 'none' && (
{formatRecurrence( task.recurrence_type )}
)}
{/* Mobile buttons on the right */}
{/* Today Plan Controls - Mobile */} {onToggleToday && ( )} {/* Play/In Progress Controls - Mobile */} {(task.status === 'not_started' || task.status === 'in_progress' || task.status === 0 || task.status === 1) && ( )} {/* Show Subtasks Controls - Mobile - Hide for completed tasks */} {hasSubtasks && !(task.status === 'done' || task.status === 2) && ( )}
); }; // Subtasks Display Component interface SubtasksDisplayProps { showSubtasks: boolean; loadingSubtasks: boolean; subtasks: Task[]; onTaskClick: (e: React.MouseEvent, task: Task) => void; } const SubtasksDisplay: React.FC = ({ showSubtasks, loadingSubtasks, subtasks, onTaskClick, }) => { const { t } = useTranslation(); if (!showSubtasks) return null; return (
{loadingSubtasks ? (
{t('loading.subtasks', 'Loading subtasks...')}
) : subtasks.length > 0 ? ( subtasks.map((subtask) => (
{ e.stopPropagation(); onTaskClick(e, subtask); }} >
{/* Left side - Task info */}
{subtask.name}
{/* Right side - Status indicator */}
{subtask.status === 'done' || subtask.status === 2 ? ( ) : null}
)) ) : (
{t('subtasks.noSubtasks', 'No subtasks found')}
)}
); }; // TaskWithSubtasks Component that combines both interface TaskWithSubtasksProps extends TaskHeaderProps { onSubtaskClick?: (subtask: Task) => void; } const TaskWithSubtasks: React.FC = (props) => { const [showSubtasks, setShowSubtasks] = useState(false); const [subtasks, setSubtasks] = useState([]); const [loadingSubtasks, setLoadingSubtasks] = useState(false); const [hasSubtasks, setHasSubtasks] = useState(false); // Check if task has subtasks on mount useEffect(() => { const checkSubtasks = async () => { if (!props.task.id) return; console.log('Checking subtasks for task:', props.task.id, props.task.name); try { const subtasksData = await fetchSubtasks(props.task.id); console.log('Subtasks data:', subtasksData); setHasSubtasks(subtasksData.length > 0); } catch (error) { console.error('Error fetching subtasks:', error); setHasSubtasks(false); } }; checkSubtasks(); }, [props.task.id]); const loadSubtasks = async () => { if (!props.task.id) return; setLoadingSubtasks(true); try { const subtasksData = await fetchSubtasks(props.task.id); setSubtasks(subtasksData); } catch (error) { console.error('Failed to load subtasks:', error); setSubtasks([]); } finally { setLoadingSubtasks(false); } }; const handleSubtasksToggle = async (e: React.MouseEvent) => { e.stopPropagation(); // Prevent opening task modal if (!showSubtasks && subtasks.length === 0) { await loadSubtasks(); } setShowSubtasks(!showSubtasks); }; return ( <> { e.stopPropagation(); // Handle subtask click - could be extended with custom logic console.log('Subtask clicked:', task); }} /> ); }; export { TaskWithSubtasks }; export default TaskHeader;