import React, { useState, useEffect } from 'react'; import { CheckSquare, Clock, AlertCircle, CheckCircle2, XCircle, Plus, Calendar, User, Play, } from 'lucide-react'; import { toast } from 'react-toastify'; import { Loading, EmptyState } from '../../components/common'; import { useAsync } from '../../hooks/useAsync'; import taskService, { Task, TaskStatistics } from '../../services/api/taskService'; import { formatDate } from '../../utils/format'; import TaskDetailModal from '../../components/tasks/TaskDetailModal'; import CreateTaskModal from '../../components/tasks/CreateTaskModal'; import TaskFilters from '../../components/tasks/TaskFilters'; import { logger } from '../../utils/logger'; type TaskStatus = 'pending' | 'assigned' | 'in_progress' | 'completed' | 'cancelled' | 'overdue'; type TaskPriority = 'low' | 'medium' | 'high' | 'urgent'; const TaskManagementPage: React.FC = () => { const [selectedTask, setSelectedTask] = useState(null); const [showTaskDetail, setShowTaskDetail] = useState(false); const [showCreateTask, setShowCreateTask] = useState(false); const [filters, setFilters] = useState({ status: '' as string, priority: '' as string, task_type: '', assigned_to: '', search: '', }); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 20; const { data: tasks, loading: tasksLoading, execute: fetchTasks } = useAsync( () => taskService.getTasks({ status: filters.status || undefined, priority: filters.priority || undefined, task_type: filters.task_type || undefined, assigned_to: filters.assigned_to ? parseInt(filters.assigned_to) : undefined, skip: (currentPage - 1) * itemsPerPage, limit: itemsPerPage, }).then(r => { // Handle response structure: { status: 'success', data: [...] } // apiClient returns axios response, so r.data is the response body const responseData = r.data; const tasksArray = responseData?.data || responseData || []; return Array.isArray(tasksArray) ? tasksArray : []; }).catch(error => { logger.error('Error fetching tasks', error); return []; }), { immediate: true } ); const { data: statistics, execute: fetchStatistics } = useAsync( async () => { const r = await taskService.getTaskStatistics(); return (r as any).data?.data || r.data; }, { immediate: true } ); useEffect(() => { fetchTasks(); }, [filters, currentPage]); const handleTaskClick = async (task: Task) => { try { const response = await taskService.getTask(task.id); setSelectedTask(response.data.data); setShowTaskDetail(true); } catch (error: any) { toast.error(error.message || 'Failed to load task details'); } }; const handleTaskComplete = async (taskId: number) => { try { await taskService.completeTask(taskId); toast.success('Task completed successfully'); fetchTasks(); fetchStatistics(); if (selectedTask?.id === taskId) { setShowTaskDetail(false); setSelectedTask(null); } } catch (error: any) { toast.error(error.message || 'Failed to complete task'); } }; const handleTaskStart = async (taskId: number) => { try { await taskService.startTask(taskId); toast.success('Task started'); fetchTasks(); if (selectedTask?.id === taskId) { const response = await taskService.getTask(taskId); setSelectedTask(response.data.data); } } catch (error: any) { toast.error(error.message || 'Failed to start task'); } }; const getStatusIcon = (status: TaskStatus) => { switch (status) { case 'completed': return ; case 'in_progress': return ; case 'overdue': return ; case 'cancelled': return ; default: return ; } }; const getStatusBadge = (status: TaskStatus) => { const baseClasses = 'px-3 py-1 rounded-full text-xs font-semibold'; switch (status) { case 'completed': return `${baseClasses} bg-green-100 text-green-800 border border-green-200`; case 'in_progress': return `${baseClasses} bg-blue-100 text-blue-800 border border-blue-200`; case 'overdue': return `${baseClasses} bg-red-100 text-red-800 border border-red-200`; case 'cancelled': return `${baseClasses} bg-gray-100 text-gray-800 border border-gray-200`; case 'assigned': return `${baseClasses} bg-purple-100 text-purple-800 border border-purple-200`; default: return `${baseClasses} bg-amber-100 text-amber-800 border border-amber-200`; } }; const getPriorityBadge = (priority: TaskPriority) => { const baseClasses = 'px-2 py-1 rounded text-xs font-semibold'; switch (priority) { case 'urgent': return `${baseClasses} bg-red-100 text-red-800 border border-red-200`; case 'high': return `${baseClasses} bg-orange-100 text-orange-800 border border-orange-200`; case 'medium': return `${baseClasses} bg-yellow-100 text-yellow-800 border border-yellow-200`; default: return `${baseClasses} bg-gray-100 text-gray-800 border border-gray-200`; } }; const isOverdue = (dueDate?: string) => { if (!dueDate) return false; return new Date(dueDate) < new Date() && !selectedTask?.completed_at; }; return (
{/* Header */}

Task Management

Manage and track all tasks and workflows

{/* Statistics */} {statistics && (

Total Tasks

{statistics.total}

In Progress

{statistics.in_progress}

Completed

{statistics.completed}

Overdue

{statistics.overdue}

)} {/* Filters */} {/* Tasks List */} {tasksLoading ? ( ) : !tasks || tasks.length === 0 ? (
setShowCreateTask(true), }} />
) : (
{(tasks || []).map((task) => ( handleTaskClick(task)} > ))}
Task Status Priority Assigned To Due Date Actions
{getStatusIcon(task.status)}

{task.title}

{task.description && (

{task.description}

)}
{task.status.replace('_', ' ')} {task.priority} {task.assigned_to_name ? (
{task.assigned_to_name}
) : ( Unassigned )}
{task.due_date ? (
{formatDate(new Date(task.due_date), 'short')}
) : ( No due date )}
{task.status === 'assigned' && ( )} {task.status === 'in_progress' && ( )}
)} {/* Pagination */} {tasks && tasks.length > 0 && (
Page {currentPage}
)}
{/* Task Detail Modal */} {showTaskDetail && selectedTask && ( { setShowTaskDetail(false); setSelectedTask(null); }} onUpdate={() => { fetchTasks(); fetchStatistics(); }} /> )} {/* Create Task Modal */} {showCreateTask && ( setShowCreateTask(false)} onSuccess={() => { setShowCreateTask(false); fetchTasks(); fetchStatistics(); }} /> )}
); }; export default TaskManagementPage;