import { useState, useEffect } from 'react'; import './TaskPanel.css'; const TaskPanel = ({ turtles, apiUrl }) => { const [tasks, setTasks] = useState([]); const [filter, setFilter] = useState('all'); // all, pending, in_progress, completed, failed const [showCreateForm, setShowCreateForm] = useState(false); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(null); // Form state const [taskType, setTaskType] = useState('mine_area'); const [priority, setPriority] = useState(5); const [assignedTurtleId, setAssignedTurtleId] = useState(''); const [parameters, setParameters] = useState({ x1: '', y1: '', z1: '', x2: '', y2: '', z2: '' }); const taskTypes = [ { value: 'mine_area', label: 'โ›๏ธ Mine Area', icon: 'โ›๏ธ' }, { value: 'explore', label: '๐Ÿ” Explore', icon: '๐Ÿ”' }, { value: 'gather', label: '๐Ÿ“ฆ Gather Resources', icon: '๐Ÿ“ฆ' }, { value: 'build', label: '๐Ÿ—๏ธ Build Structure', icon: '๐Ÿ—๏ธ' }, { value: 'transport', label: '๐Ÿšš Transport Items', icon: '๐Ÿšš' }, { value: 'clear_area', label: '๐Ÿงน Clear Area', icon: '๐Ÿงน' }, ]; useEffect(() => { loadTasks(); // Refresh tasks every 10 seconds const interval = setInterval(loadTasks, 10000); return () => clearInterval(interval); }, [filter]); const loadTasks = async () => { setLoading(true); try { const url = filter === 'all' ? `${apiUrl}/api/tasks` : `${apiUrl}/api/tasks?status=${filter}`; const response = await fetch(url); if (response.ok) { const data = await response.json(); // Server returns flat array of formatted tasks setTasks(Array.isArray(data) ? data : (data.tasks || [])); } } catch (error) { console.error('Failed to load tasks:', error); } finally { setLoading(false); } }; const createTask = async (e) => { e.preventDefault(); // Validate coordinates const coords = ['x1', 'y1', 'z1', 'x2', 'y2', 'z2']; const hasCoords = coords.some(key => parameters[key] !== ''); if (hasCoords && !coords.every(key => parameters[key] !== '')) { showMessage('Please fill all coordinates or leave them empty', 'error'); return; } const taskData = { taskType, priority: parseInt(priority), assignedTurtleId: assignedTurtleId ? parseInt(assignedTurtleId) : null, parameters: hasCoords ? { x1: parseInt(parameters.x1), y1: parseInt(parameters.y1), z1: parseInt(parameters.z1), x2: parseInt(parameters.x2), y2: parseInt(parameters.y2), z2: parseInt(parameters.z2) } : {} }; try { const response = await fetch(`${apiUrl}/api/tasks`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(taskData) }); if (response.ok) { showMessage('Task created successfully!', 'success'); setShowCreateForm(false); resetForm(); loadTasks(); } else { showMessage('Failed to create task', 'error'); } } catch (error) { showMessage('Error creating task', 'error'); } }; const updateTaskStatus = async (taskId, status, result = null) => { try { const response = await fetch(`${apiUrl}/api/tasks/${taskId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status, result }) }); if (response.ok) { showMessage('Task updated', 'success'); loadTasks(); } else { showMessage('Failed to update task', 'error'); } } catch (error) { showMessage('Error updating task', 'error'); } }; const deleteTask = async (taskId) => { if (!confirm('Are you sure you want to delete this task?')) return; try { const response = await fetch(`${apiUrl}/api/tasks/${taskId}`, { method: 'DELETE' }); if (response.ok) { showMessage('Task deleted', 'success'); loadTasks(); } else { showMessage('Failed to delete task', 'error'); } } catch (error) { showMessage('Error deleting task', 'error'); } }; const resetForm = () => { setTaskType('mine_area'); setPriority(5); setAssignedTurtleId(''); setParameters({ x1: '', y1: '', z1: '', x2: '', y2: '', z2: '' }); }; const showMessage = (text, type) => { setMessage({ text, type }); setTimeout(() => setMessage(null), 3000); }; const getTaskIcon = (type) => { const taskType = taskTypes.find(t => t.value === type); return taskType ? taskType.icon : '๐Ÿ“‹'; }; const getStatusColor = (status) => { switch (status) { case 'pending': return '#94a3b8'; case 'in_progress': return '#3b82f6'; case 'completed': return '#10b981'; case 'failed': return '#ef4444'; default: return '#94a3b8'; } }; const getPriorityLabel = (priority) => { if (priority >= 8) return { label: 'Critical', color: '#ef4444' }; if (priority >= 6) return { label: 'High', color: '#f59e0b' }; if (priority >= 4) return { label: 'Medium', color: '#3b82f6' }; return { label: 'Low', color: '#94a3b8' }; }; return (

๐Ÿ“‹ Task Queue

{message && (
{message.text}
)} {/* Create Task Form */} {showCreateForm && (

Create New Task

Coordinates (optional):

Start: setParameters({...parameters, x1: e.target.value})} /> setParameters({...parameters, y1: e.target.value})} /> setParameters({...parameters, z1: e.target.value})} />
End: setParameters({...parameters, x2: e.target.value})} /> setParameters({...parameters, y2: e.target.value})} /> setParameters({...parameters, z2: e.target.value})} />
)} {/* Filter Tabs */}
{/* Tasks List */}
{loading &&
Loading tasks...
} {!loading && tasks.length === 0 && (
๐Ÿ“‹
No Tasks Found
{filter === 'all' ? 'Create a task to get started!' : `No ${filter.replace('_', ' ')} tasks`}
)} {tasks.map(task => { const priorityInfo = getPriorityLabel(task.priority); const turtle = turtles.find(t => t.turtleID === task.assignedTurtleId); return (
{getTaskIcon(task.taskType)} {taskTypes.find(t => t.value === task.taskType)?.label || task.taskType}
{priorityInfo.label} {task.status.replace('_', ' ')}
{task.assignedTurtleId && (
๐Ÿข {turtle?.name || `Turtle ${task.assignedTurtleId}`}
)} {task.parameters && Object.keys(task.parameters).length > 0 && (
Area: ({task.parameters.x1}, {task.parameters.y1}, {task.parameters.z1}) โ†’ ({task.parameters.x2}, {task.parameters.y2}, {task.parameters.z2})
)} {task.result && (
Result: {task.result}
)}
{task.status === 'pending' && ( <> )} {task.status === 'in_progress' && ( <> )} {(task.status === 'completed' || task.status === 'failed') && ( )}
Created: {new Date(task.createdAt).toLocaleString()}
); })}
); }; export default TaskPanel;