import { useState, useEffect, useCallback, useRef } from "react"; import { X, Plus, Pencil, Trash2, ChevronRight, ChevronDown, ChevronLeft, Target, Layers, Package, Box, Check, Loader2, Link, Unlink, Play, Pause, Square, RefreshCw, Sparkles, Zap, } from "lucide-react"; import type { ToastType } from "../hooks/useToast"; import { MissionInterviewModal } from "./MissionInterviewModal"; import type { Mission, MissionWithHierarchy, MissionWithSummary, Milestone, Slice, MissionFeature, MissionStatus, MilestoneStatus, SliceStatus, FeatureStatus, MilestoneWithSlices, SliceWithFeatures, } from "./mission-types"; import { fetchMissions, createMission, fetchMission, updateMission, deleteMission, createMilestone, updateMilestone, deleteMilestone, createSlice, updateSlice, deleteSlice, activateSlice, createFeature, updateFeature, deleteFeature, linkFeatureToTask, unlinkFeatureFromTask, triageFeature, triageAllSliceFeatures, pauseMission, resumeMission, stopMission, startMission, fetchMissionAutopilotStatus, updateMissionAutopilot, startMissionAutopilot, stopMissionAutopilot, } from "../api"; import type { AutopilotStatus as AutopilotStatusType, AutopilotState } from "./mission-types"; interface MissionManagerProps { isOpen: boolean; onClose: () => void; addToast: (message: string, type?: ToastType) => void; projectId?: string; onSelectTask?: (taskId: string) => void; availableTasks?: Array<{ id: string; title?: string }>; resumeSessionId?: string; /** Pre-select and load this mission when the modal opens */ targetMissionId?: string; } // Status badge colors — use CSS custom-property-compatible tokens const missionStatusColors: Record = { planning: { bg: "var(--mission-planning-bg)", text: "var(--mission-planning-text)" }, active: { bg: "var(--mission-active-bg)", text: "var(--mission-active-text)" }, blocked: { bg: "var(--mission-blocked-bg)", text: "var(--mission-blocked-text)" }, complete: { bg: "var(--mission-complete-bg)", text: "var(--mission-complete-text)" }, archived: { bg: "var(--mission-archived-bg)", text: "var(--mission-archived-text)" }, }; const milestoneStatusColors: Record = { planning: { bg: "var(--mission-planning-bg)", text: "var(--mission-planning-text)" }, active: { bg: "var(--mission-active-bg)", text: "var(--mission-active-text)" }, blocked: { bg: "var(--mission-blocked-bg)", text: "var(--mission-blocked-text)" }, complete: { bg: "var(--mission-complete-bg)", text: "var(--mission-complete-text)" }, }; const sliceStatusColors: Record = { pending: { bg: "var(--slice-pending-bg)", text: "var(--slice-pending-text)" }, active: { bg: "var(--slice-active-bg)", text: "var(--slice-active-text)" }, complete: { bg: "var(--slice-complete-bg)", text: "var(--slice-complete-text)" }, }; const featureStatusColors: Record = { defined: { bg: "var(--feature-defined-bg)", text: "var(--feature-defined-text)" }, triaged: { bg: "var(--feature-triaged-bg)", text: "var(--feature-triaged-text)" }, "in-progress": { bg: "var(--feature-in-progress-bg)", text: "var(--feature-in-progress-text)" }, done: { bg: "var(--feature-done-bg)", text: "var(--feature-done-text)" }, }; const autopilotStateColors: Record = { inactive: { bg: "var(--autopilot-inactive-bg)", text: "var(--autopilot-inactive-text)" }, watching: { bg: "var(--autopilot-watching-bg)", text: "var(--autopilot-watching-text)" }, activating: { bg: "var(--autopilot-activating-bg)", text: "var(--autopilot-activating-text)" }, completing: { bg: "var(--autopilot-completing-bg)", text: "var(--autopilot-completing-text)" }, }; // Form types interface MissionFormData { title: string; description: string; status: MissionStatus; autoAdvance: boolean; autopilotEnabled: boolean; } interface MilestoneFormData { title: string; description: string; status: MilestoneStatus; dependencies: string[]; } interface SliceFormData { title: string; description: string; status: SliceStatus; } interface FeatureFormData { title: string; description: string; acceptanceCriteria: string; status: FeatureStatus; } const EMPTY_MISSION_FORM: MissionFormData = { title: "", description: "", status: "planning", autoAdvance: false, autopilotEnabled: false, }; const EMPTY_MILESTONE_FORM: MilestoneFormData = { title: "", description: "", status: "planning", dependencies: [], }; const EMPTY_SLICE_FORM: SliceFormData = { title: "", description: "", status: "pending", }; const EMPTY_FEATURE_FORM: FeatureFormData = { title: "", description: "", acceptanceCriteria: "", status: "defined", }; export function MissionManager({ isOpen, onClose, addToast, projectId, onSelectTask, availableTasks = [], resumeSessionId, targetMissionId }: MissionManagerProps) { const [missions, setMissions] = useState([]); const [selectedMission, setSelectedMission] = useState(null); const [loading, setLoading] = useState(true); const [detailLoading, setDetailLoading] = useState(false); // Form states const [isCreatingMission, setIsCreatingMission] = useState(false); const [editingMissionId, setEditingMissionId] = useState(null); const [missionForm, setMissionForm] = useState(EMPTY_MISSION_FORM); const [saving, setSaving] = useState(false); const [expandedMilestones, setExpandedMilestones] = useState>(new Set()); const [expandedSlices, setExpandedSlices] = useState>(new Set()); // Editing states for nested items const [editingMilestoneId, setEditingMilestoneId] = useState(null); const [milestoneForm, setMilestoneForm] = useState(EMPTY_MILESTONE_FORM); const [isCreatingMilestone, setIsCreatingMilestone] = useState(false); const [editingSliceId, setEditingSliceId] = useState(null); const [sliceForm, setSliceForm] = useState(EMPTY_SLICE_FORM); const [isCreatingSlice, setIsCreatingSlice] = useState(false); const [selectedMilestoneIdForNewSlice, setSelectedMilestoneIdForNewSlice] = useState(null); const [editingFeatureId, setEditingFeatureId] = useState(null); const [featureForm, setFeatureForm] = useState(EMPTY_FEATURE_FORM); const [isCreatingFeature, setIsCreatingFeature] = useState(false); const [selectedSliceIdForNewFeature, setSelectedSliceIdForNewFeature] = useState(null); // Link task modal state const [linkTaskFeatureId, setLinkTaskFeatureId] = useState(null); const [selectedTaskId, setSelectedTaskId] = useState(""); // AI Interview modal const [showInterviewModal, setShowInterviewModal] = useState(false); // Auto-open interview modal when resuming a session useEffect(() => { if (isOpen && resumeSessionId) { setShowInterviewModal(true); } }, [isOpen, resumeSessionId]); // Delete confirmation const [deleteConfirmId, setDeleteConfirmId] = useState<{ type: string; id: string } | null>(null); // Autopilot state const [autopilotStatus, setAutopilotStatus] = useState(null); const [autopilotLoading, setAutopilotLoading] = useState(false); const loadMissions = useCallback(async () => { try { setLoading(true); const data = await fetchMissions(projectId); setMissions(data); } catch (err: any) { addToast(err.message || "Failed to load missions", "error"); } finally { setLoading(false); } }, [addToast, projectId]); const loadMissionDetail = useCallback(async (missionId: string) => { try { setDetailLoading(true); const data = await fetchMission(missionId, projectId); setSelectedMission(data); // Auto-expand first milestone and slice if (data.milestones.length > 0) { setExpandedMilestones(new Set([data.milestones[0].id])); if (data.milestones[0].slices.length > 0) { setExpandedSlices(new Set([data.milestones[0].slices[0].id])); } } } catch (err: any) { addToast(err.message || "Failed to load mission details", "error"); } finally { setDetailLoading(false); } }, [addToast, projectId]); useEffect(() => { if (isOpen) { loadMissions(); setSelectedMission(null); } }, [isOpen, loadMissions]); // Auto-load target mission when specified const targetLoadedRef = useRef(null); useEffect(() => { if (isOpen && targetMissionId && targetLoadedRef.current !== targetMissionId && missions.length > 0) { targetLoadedRef.current = targetMissionId; loadMissionDetail(targetMissionId); } }, [isOpen, targetMissionId, missions, loadMissionDetail]); // Reset target tracking when modal closes useEffect(() => { if (!isOpen) { targetLoadedRef.current = null; } }, [isOpen]); // Mission handlers const handleCreateMission = useCallback(() => { setIsCreatingMission(true); setEditingMissionId(null); setMissionForm(EMPTY_MISSION_FORM); }, []); const handleEditMission = useCallback((mission: Mission) => { setEditingMissionId(mission.id); setIsCreatingMission(false); setMissionForm({ title: mission.title, description: mission.description || "", status: mission.status, autoAdvance: mission.autoAdvance ?? false, autopilotEnabled: mission.autopilotEnabled ?? false, }); }, []); const handleCancelMission = useCallback(() => { setEditingMissionId(null); setIsCreatingMission(false); setMissionForm(EMPTY_MISSION_FORM); }, []); const handleSaveMission = useCallback(async () => { if (!missionForm.title.trim()) { addToast("Mission title is required", "error"); return; } try { setSaving(true); if (isCreatingMission) { await createMission({ title: missionForm.title.trim(), description: missionForm.description.trim() || undefined, autopilotEnabled: missionForm.autopilotEnabled, }, projectId); addToast("Mission created", "success"); } else if (editingMissionId) { await updateMission(editingMissionId, { title: missionForm.title.trim(), description: missionForm.description.trim() || undefined, status: missionForm.status, autoAdvance: missionForm.autoAdvance, autopilotEnabled: missionForm.autopilotEnabled, }, projectId); addToast("Mission updated", "success"); // Refresh detail view if viewing this mission if (selectedMission?.id === editingMissionId) { await loadMissionDetail(editingMissionId); } } await loadMissions(); handleCancelMission(); } catch (err: any) { addToast(err.message || "Failed to save mission", "error"); } finally { setSaving(false); } }, [missionForm, isCreatingMission, editingMissionId, addToast, loadMissions, loadMissionDetail, selectedMission, handleCancelMission, projectId]); const handleDeleteMission = useCallback(async (missionId: string) => { try { await deleteMission(missionId, projectId); addToast("Mission deleted", "success"); if (selectedMission?.id === missionId) { setSelectedMission(null); } await loadMissions(); setDeleteConfirmId(null); } catch (err: any) { addToast(err.message || "Failed to delete mission", "error"); } }, [addToast, loadMissions, selectedMission, projectId]); // Milestone handlers const handleCreateMilestone = useCallback(() => { setIsCreatingMilestone(true); setEditingMilestoneId(null); setMilestoneForm(EMPTY_MILESTONE_FORM); }, []); const handleEditMilestone = useCallback((milestone: Milestone) => { setEditingMilestoneId(milestone.id); setIsCreatingMilestone(false); setMilestoneForm({ title: milestone.title, description: milestone.description || "", status: milestone.status, dependencies: milestone.dependencies, }); }, []); const handleCancelMilestone = useCallback(() => { setEditingMilestoneId(null); setIsCreatingMilestone(false); setMilestoneForm(EMPTY_MILESTONE_FORM); }, []); const handleSaveMilestone = useCallback(async () => { if (!milestoneForm.title.trim()) { addToast("Milestone title is required", "error"); return; } try { setSaving(true); if (isCreatingMilestone && selectedMission) { await createMilestone(selectedMission.id, { title: milestoneForm.title.trim(), description: milestoneForm.description.trim() || undefined, dependencies: milestoneForm.dependencies, }, projectId); addToast("Milestone created", "success"); } else if (editingMilestoneId) { await updateMilestone(editingMilestoneId, { title: milestoneForm.title.trim(), description: milestoneForm.description.trim() || undefined, status: milestoneForm.status, dependencies: milestoneForm.dependencies, }, projectId); addToast("Milestone updated", "success"); } await loadMissionDetail(selectedMission!.id); handleCancelMilestone(); } catch (err: any) { addToast(err.message || "Failed to save milestone", "error"); } finally { setSaving(false); } }, [milestoneForm, isCreatingMilestone, editingMilestoneId, selectedMission, addToast, loadMissionDetail, handleCancelMilestone, missionForm.title, projectId]); const handleDeleteMilestone = useCallback(async (milestoneId: string) => { try { await deleteMilestone(milestoneId, projectId); addToast("Milestone deleted", "success"); await loadMissionDetail(selectedMission!.id); setDeleteConfirmId(null); } catch (err: any) { addToast(err.message || "Failed to delete milestone", "error"); } }, [addToast, loadMissionDetail, selectedMission, projectId]); const toggleMilestoneExpanded = useCallback((milestoneId: string) => { setExpandedMilestones((prev) => { const next = new Set(prev); if (next.has(milestoneId)) { next.delete(milestoneId); } else { next.add(milestoneId); } return next; }); }, []); // Slice handlers const handleCreateSlice = useCallback((milestoneId: string) => { setSelectedMilestoneIdForNewSlice(milestoneId); setIsCreatingSlice(true); setEditingSliceId(null); setSliceForm(EMPTY_SLICE_FORM); }, []); const handleEditSlice = useCallback((slice: Slice) => { setEditingSliceId(slice.id); setIsCreatingSlice(false); setSliceForm({ title: slice.title, description: slice.description || "", status: slice.status, }); }, []); const handleCancelSlice = useCallback(() => { setEditingSliceId(null); setIsCreatingSlice(false); setSelectedMilestoneIdForNewSlice(null); setSliceForm(EMPTY_SLICE_FORM); }, []); const handleSaveSlice = useCallback(async () => { if (!sliceForm.title.trim()) { addToast("Slice title is required", "error"); return; } try { setSaving(true); if (isCreatingSlice && selectedMilestoneIdForNewSlice) { await createSlice(selectedMilestoneIdForNewSlice, { title: sliceForm.title.trim(), description: sliceForm.description.trim() || undefined, }, projectId); addToast("Slice created", "success"); } else if (editingSliceId) { await updateSlice(editingSliceId, { title: sliceForm.title.trim(), description: sliceForm.description.trim() || undefined, status: sliceForm.status, }, projectId); addToast("Slice updated", "success"); } await loadMissionDetail(selectedMission!.id); handleCancelSlice(); } catch (err: any) { addToast(err.message || "Failed to save slice", "error"); } finally { setSaving(false); } }, [sliceForm, isCreatingSlice, editingSliceId, selectedMilestoneIdForNewSlice, selectedMission, addToast, loadMissionDetail, handleCancelSlice, projectId]); const handleDeleteSlice = useCallback(async (sliceId: string) => { try { await deleteSlice(sliceId, projectId); addToast("Slice deleted", "success"); await loadMissionDetail(selectedMission!.id); setDeleteConfirmId(null); } catch (err: any) { addToast(err.message || "Failed to delete slice", "error"); } }, [addToast, loadMissionDetail, selectedMission, projectId]); const handleActivateSlice = useCallback(async (sliceId: string) => { try { await activateSlice(sliceId, projectId); addToast("Slice activated", "success"); await loadMissionDetail(selectedMission!.id); } catch (err: any) { addToast(err.message || "Failed to activate slice", "error"); } }, [addToast, loadMissionDetail, selectedMission, projectId]); const toggleSliceExpanded = useCallback((sliceId: string) => { setExpandedSlices((prev) => { const next = new Set(prev); if (next.has(sliceId)) { next.delete(sliceId); } else { next.add(sliceId); } return next; }); }, []); // Feature handlers const handleCreateFeature = useCallback((sliceId: string) => { setSelectedSliceIdForNewFeature(sliceId); setIsCreatingFeature(true); setEditingFeatureId(null); setFeatureForm(EMPTY_FEATURE_FORM); }, []); const handleEditFeature = useCallback((feature: MissionFeature) => { setEditingFeatureId(feature.id); setIsCreatingFeature(false); setFeatureForm({ title: feature.title, description: feature.description || "", acceptanceCriteria: feature.acceptanceCriteria || "", status: feature.status, }); }, []); const handleCancelFeature = useCallback(() => { setEditingFeatureId(null); setIsCreatingFeature(false); setSelectedSliceIdForNewFeature(null); setFeatureForm(EMPTY_FEATURE_FORM); }, []); const handleSaveFeature = useCallback(async () => { if (!featureForm.title.trim()) { addToast("Feature title is required", "error"); return; } try { setSaving(true); if (isCreatingFeature && selectedSliceIdForNewFeature) { await createFeature(selectedSliceIdForNewFeature, { title: featureForm.title.trim(), description: featureForm.description.trim() || undefined, acceptanceCriteria: featureForm.acceptanceCriteria.trim() || undefined, }, projectId); addToast("Feature created", "success"); } else if (editingFeatureId) { await updateFeature(editingFeatureId, { title: featureForm.title.trim(), description: featureForm.description.trim() || undefined, acceptanceCriteria: featureForm.acceptanceCriteria.trim() || undefined, status: featureForm.status, }, projectId); addToast("Feature updated", "success"); } await loadMissionDetail(selectedMission!.id); handleCancelFeature(); } catch (err: any) { addToast(err.message || "Failed to save feature", "error"); } finally { setSaving(false); } }, [featureForm, isCreatingFeature, editingFeatureId, selectedSliceIdForNewFeature, selectedMission, addToast, loadMissionDetail, handleCancelFeature, projectId]); const handleDeleteFeature = useCallback(async (featureId: string) => { try { await deleteFeature(featureId, projectId); addToast("Feature deleted", "success"); await loadMissionDetail(selectedMission!.id); setDeleteConfirmId(null); } catch (err: any) { addToast(err.message || "Failed to delete feature", "error"); } }, [addToast, loadMissionDetail, selectedMission, projectId]); const handleLinkTask = useCallback(async () => { if (!linkTaskFeatureId || !selectedTaskId.trim()) { addToast("Task ID is required", "error"); return; } try { await linkFeatureToTask(linkTaskFeatureId, selectedTaskId.trim(), projectId); addToast("Feature linked to task", "success"); await loadMissionDetail(selectedMission!.id); setLinkTaskFeatureId(null); setSelectedTaskId(""); } catch (err: any) { addToast(err.message || "Failed to link feature to task", "error"); } }, [linkTaskFeatureId, selectedTaskId, addToast, loadMissionDetail, selectedMission, projectId]); const handleUnlinkTask = useCallback(async (featureId: string) => { try { await unlinkFeatureFromTask(featureId, projectId); addToast("Feature unlinked from task", "success"); await loadMissionDetail(selectedMission!.id); } catch (err: any) { addToast(err.message || "Failed to unlink feature", "error"); } }, [addToast, loadMissionDetail, selectedMission, projectId]); // Triage a single feature — creates a task and links it const handleTriageFeature = useCallback(async (featureId: string) => { try { setSaving(true); await triageFeature(featureId, undefined, undefined, projectId); addToast("Feature triaged — task created", "success"); await loadMissionDetail(selectedMission!.id); } catch (err: any) { addToast(err.message || "Failed to triage feature", "error"); } finally { setSaving(false); } }, [addToast, loadMissionDetail, selectedMission, projectId]); // Triage all defined features in a slice const handleTriageAllSliceFeatures = useCallback(async (sliceId: string) => { try { setSaving(true); const result = await triageAllSliceFeatures(sliceId, projectId); addToast(`Triaged ${result.count} feature${result.count !== 1 ? "s" : ""}`, "success"); await loadMissionDetail(selectedMission!.id); } catch (err: any) { addToast(err.message || "Failed to triage slice features", "error"); } finally { setSaving(false); } }, [addToast, loadMissionDetail, selectedMission, projectId]); // Pause mission — set status to "blocked" const handlePauseMission = useCallback(async (missionId: string) => { try { await pauseMission(missionId, projectId); addToast("Mission paused", "success"); await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to pause mission", "error"); } }, [addToast, loadMissionDetail, loadMissions, projectId]); // Resume a paused mission — set status back to "active" const handleResumeMission = useCallback(async (missionId: string) => { try { await resumeMission(missionId, projectId); addToast("Mission resumed", "success"); await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to resume mission", "error"); } }, [addToast, loadMissionDetail, loadMissions, projectId]); // Stop mission — set status to "blocked" and pause all linked tasks const handleStopMission = useCallback(async (missionId: string) => { try { const result = await stopMission(missionId, projectId); const count = result.pausedTaskIds?.length ?? 0; addToast(`Mission stopped (${count} task${count !== 1 ? "s" : ""} paused)`, "success"); await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to stop mission", "error"); } }, [addToast, loadMissionDetail, loadMissions, projectId]); // Start a planning mission — set status to "active" and activate first slice const handleStartMission = useCallback(async (missionId: string) => { try { await startMission(missionId, projectId); addToast("Mission started — first slice activated", "success"); await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to start mission", "error"); } }, [addToast, loadMissionDetail, loadMissions, projectId]); // ── Autopilot handlers ── const loadAutopilotStatus = useCallback(async (missionId: string) => { try { const status = await fetchMissionAutopilotStatus(missionId, projectId); setAutopilotStatus(status); } catch { // Silently ignore — autopilot status is supplementary } }, [projectId]); const handleToggleAutopilot = useCallback(async (missionId: string, enabled: boolean) => { try { setAutopilotLoading(true); const status = await updateMissionAutopilot(missionId, { enabled }, projectId); setAutopilotStatus(status); addToast(enabled ? "Autopilot enabled" : "Autopilot disabled", "success"); // Reload mission detail to reflect updated fields await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to update autopilot", "error"); } finally { setAutopilotLoading(false); } }, [addToast, loadMissionDetail, loadMissions, projectId]); const handleStartAutopilot = useCallback(async (missionId: string) => { try { setAutopilotLoading(true); const status = await startMissionAutopilot(missionId, projectId); setAutopilotStatus(status); addToast("Autopilot started", "success"); await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to start autopilot", "error"); } finally { setAutopilotLoading(false); } }, [addToast, loadMissionDetail, loadMissions, projectId]); const handleStopAutopilot = useCallback(async (missionId: string) => { try { setAutopilotLoading(true); const status = await stopMissionAutopilot(missionId, projectId); setAutopilotStatus(status); addToast("Autopilot stopped", "success"); await loadMissionDetail(missionId); loadMissions(); } catch (err: any) { addToast(err.message || "Failed to stop autopilot", "error"); } finally { setAutopilotLoading(false); } }, [addToast, loadMissionDetail, loadMissions, projectId]); const handleSelectMission = useCallback((mission: Mission) => { loadMissionDetail(mission.id); loadAutopilotStatus(mission.id); }, [loadMissionDetail, loadAutopilotStatus]); const handleBackToList = useCallback(() => { setSelectedMission(null); setAutopilotStatus(null); loadMissions(); }, [loadMissions]); // Keyboard handler for mission form const handleMissionFormKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSaveMission(); } }, [handleSaveMission]); const handleMilestoneFormKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSaveMilestone(); } }, [handleSaveMilestone]); const handleSliceFormKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSaveSlice(); } }, [handleSaveSlice]); const handleFeatureFormKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleSaveFeature(); } }, [handleSaveFeature]); // Ref for focus management const modalRef = useRef(null); // Escape key handling useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose(); } }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [isOpen, onClose]); if (!isOpen) return null; return (
e.target === e.currentTarget && onClose()} data-testid="mission-manager-overlay" >
{/* ── Header ── */}
{selectedMission ? ( ) : null}

{selectedMission ? selectedMission.title : "Missions"}

{/* ── Body ── */}
{loading ? (
Loading missions...
) : detailLoading ? (
Loading mission details...
) : selectedMission ? ( /* ── Detail View ── */
{(autopilotStatus?.watched || selectedMission.autopilotState === "watching" || selectedMission.autopilotState === "activating") && ( )}

{selectedMission.title}

{selectedMission.status}
{selectedMission.description && (

{selectedMission.description}

)}
{selectedMission.autoAdvance && ( Auto-advance )} {selectedMission.milestones.length} milestones
{/* ── Autopilot section ── */}
{(selectedMission.autopilotState || autopilotStatus?.state) && ( {(autopilotStatus?.watched || selectedMission.autopilotState === "watching" || selectedMission.autopilotState === "activating") && ( )} {autopilotStatus?.state ?? selectedMission.autopilotState ?? "inactive"} )}
{autopilotStatus?.lastActivityAt && ( Last activity: {new Date(autopilotStatus.lastActivityAt).toLocaleTimeString()} )}
{selectedMission.autopilotEnabled && !autopilotStatus?.watched && ( )} {autopilotStatus?.watched && ( )}
{selectedMission.status === "active" && ( <> )} {selectedMission.status === "blocked" && ( )} {selectedMission.status === "planning" && ( )}
{/* Inline edit mission form (detail view) */} {editingMissionId === selectedMission.id && (
setMissionForm({ ...missionForm, title: e.target.value })} onKeyDown={handleMissionFormKeyDown} autoFocus />