import { useCallback, useContext, useEffect, useState } from 'react'; import { Play } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useConfig } from '@/components/config-provider'; import { attemptsApi, projectsApi } from '@/lib/api'; import type { GitBranch, TaskAttempt } from 'shared/types'; import { EXECUTOR_TYPES, EXECUTOR_LABELS } from 'shared/types'; import { TaskAttemptDataContext, TaskAttemptLoadingContext, TaskAttemptStoppingContext, TaskDetailsContext, TaskExecutionStateContext, TaskSelectedAttemptContext, } from '@/components/context/taskDetailsContext.ts'; import CreatePRDialog from '@/components/tasks/Toolbar/CreatePRDialog.tsx'; import CreateAttempt from '@/components/tasks/Toolbar/CreateAttempt.tsx'; import CurrentAttempt from '@/components/tasks/Toolbar/CurrentAttempt.tsx'; const availableExecutors = EXECUTOR_TYPES.map((id) => ({ id, name: EXECUTOR_LABELS[id] || id, })); function TaskDetailsToolbar() { const { task, projectId } = useContext(TaskDetailsContext); const { setLoading } = useContext(TaskAttemptLoadingContext); const { selectedAttempt, setSelectedAttempt } = useContext( TaskSelectedAttemptContext ); const { isStopping } = useContext(TaskAttemptStoppingContext); const { fetchAttemptData, setAttemptData, isAttemptRunning } = useContext( TaskAttemptDataContext ); const { fetchExecutionState } = useContext(TaskExecutionStateContext); const [taskAttempts, setTaskAttempts] = useState([]); const { config } = useConfig(); const [branches, setBranches] = useState([]); const [selectedBranch, setSelectedBranch] = useState(null); const [selectedExecutor, setSelectedExecutor] = useState( config?.executor.type || 'claude' ); // State for create attempt mode const [isInCreateAttemptMode, setIsInCreateAttemptMode] = useState(false); const [createAttemptBranch, setCreateAttemptBranch] = useState( selectedBranch ); const [createAttemptExecutor, setCreateAttemptExecutor] = useState(selectedExecutor); // Branch status and git operations state const [creatingPR, setCreatingPR] = useState(false); const [showCreatePRDialog, setShowCreatePRDialog] = useState(false); const [error, setError] = useState(null); const fetchProjectBranches = useCallback(async () => { const result = await projectsApi.getBranches(projectId); setBranches(result); // Set current branch as default const currentBranch = result.find((b) => b.is_current); if (currentBranch && !selectedBranch) { setSelectedBranch(currentBranch.name); } }, [projectId, selectedBranch]); useEffect(() => { fetchProjectBranches(); }, [fetchProjectBranches]); // Set default executor from config useEffect(() => { if (config && config.executor.type !== selectedExecutor) { setSelectedExecutor(config.executor.type); } }, [config, selectedExecutor]); // Set create attempt mode when there are no attempts useEffect(() => { setIsInCreateAttemptMode(taskAttempts.length === 0); }, [taskAttempts.length]); // Update default values from latest attempt when taskAttempts change useEffect(() => { if (taskAttempts.length > 0) { const latestAttempt = taskAttempts.reduce((latest, current) => new Date(current.created_at) > new Date(latest.created_at) ? current : latest ); // Only update if branch still exists in available branches if ( latestAttempt.base_branch && branches.some((b: GitBranch) => b.name === latestAttempt.base_branch) ) { setCreateAttemptBranch(latestAttempt.base_branch); } // Only update executor if it's different from default and exists in available executors if ( latestAttempt.executor && availableExecutors.some((e) => e.id === latestAttempt.executor) ) { setCreateAttemptExecutor(latestAttempt.executor); } } }, [taskAttempts, branches, availableExecutors]); const fetchTaskAttempts = useCallback(async () => { if (!task) return; try { setLoading(true); const result = await attemptsApi.getAll(projectId, task.id); setTaskAttempts((prev) => { if (JSON.stringify(prev) === JSON.stringify(result)) return prev; return result || prev; }); if (result.length > 0) { const latestAttempt = result.reduce((latest, current) => new Date(current.created_at) > new Date(latest.created_at) ? current : latest ); setSelectedAttempt((prev) => { if (JSON.stringify(prev) === JSON.stringify(latestAttempt)) return prev; return latestAttempt; }); fetchAttemptData(latestAttempt.id, latestAttempt.task_id); fetchExecutionState(latestAttempt.id, latestAttempt.task_id); } else { setSelectedAttempt(null); setAttemptData({ activities: [], processes: [], runningProcessDetails: {}, }); } } catch (error) { // we already logged error } finally { setLoading(false); } }, [task, projectId, fetchAttemptData, fetchExecutionState]); useEffect(() => { fetchTaskAttempts(); }, [fetchTaskAttempts]); // Handle entering create attempt mode const handleEnterCreateAttemptMode = useCallback(() => { setIsInCreateAttemptMode(true); // Use latest attempt's settings as defaults if available if (taskAttempts.length > 0) { const latestAttempt = taskAttempts.reduce((latest, current) => new Date(current.created_at) > new Date(latest.created_at) ? current : latest ); // Use latest attempt's branch if it still exists, otherwise use current selected branch if ( latestAttempt.base_branch && branches.some((b: GitBranch) => b.name === latestAttempt.base_branch) ) { setCreateAttemptBranch(latestAttempt.base_branch); } else { setCreateAttemptBranch(selectedBranch); } // Use latest attempt's executor if it exists, otherwise use current selected executor if ( latestAttempt.executor && availableExecutors.some((e) => e.id === latestAttempt.executor) ) { setCreateAttemptExecutor(latestAttempt.executor); } else { setCreateAttemptExecutor(selectedExecutor); } } else { // Fallback to current selected values if no attempts exist setCreateAttemptBranch(selectedBranch); setCreateAttemptExecutor(selectedExecutor); } }, [taskAttempts, branches, selectedBranch, selectedExecutor]); return ( <>
{/* Error Display */} {error && (
{error}
)} {isInCreateAttemptMode ? ( ) : (
{/* Current Attempt Info */}
{selectedAttempt ? ( ) : (
No attempts yet
Start your first attempt to begin working on this task
)}
{/* Special Actions */} {!selectedAttempt && !isAttemptRunning && !isStopping && (
)}
)}
); } export default TaskDetailsToolbar;