diff options
Diffstat (limited to 'frontend/src/components/document/DocumentLayout.tsx')
| -rw-r--r-- | frontend/src/components/document/DocumentLayout.tsx | 316 |
1 files changed, 0 insertions, 316 deletions
diff --git a/frontend/src/components/document/DocumentLayout.tsx b/frontend/src/components/document/DocumentLayout.tsx deleted file mode 100644 index 05f4190..0000000 --- a/frontend/src/components/document/DocumentLayout.tsx +++ /dev/null @@ -1,316 +0,0 @@ -import { useEffect, useState, useCallback, useRef, useMemo } from 'react' -import { useParams, useNavigate, Link } from 'react-router-dom' -import { DirectiveFileTree } from './DirectiveFileTree' -import DocumentEditor from './DocumentEditor' -import { ToastProvider, useToast } from './Toast' -import { - type DirectiveWithSteps, - type DirectiveStep, - getDirective, - getDirectiveSteps, - updateGoal, - updateDirective, - cleanupDirective, - createPr, - pickUpOrders, - pauseDirective, - startDirective, -} from '../../services/directiveApi' -import './DocumentLayout.css' - -function StatusBadge({ status }: { status: string }) { - const colors: Record<string, string> = { - active: '#4caf50', - running: '#4caf50', - idle: '#ffc107', - paused: '#ffc107', - draft: '#9e9e9e', - pending: '#9e9e9e', - archived: '#f44336', - failed: '#f44336', - } - const color = colors[status.toLowerCase()] || '#9e9e9e' - - return ( - <span className="doc-status-badge" style={{ backgroundColor: color }}> - {status} - </span> - ) -} - -function DocumentLayoutInner() { - const { id: urlDirectiveId } = useParams<{ id: string }>() - const navigate = useNavigate() - const { addToast } = useToast() - - const [selectedId, setSelectedId] = useState<string | null>(urlDirectiveId || null) - const [directive, setDirective] = useState<DirectiveWithSteps | null>(null) - const [loading, setLoading] = useState(false) - const [error, setError] = useState<string | null>(null) - const [sidebarWidth, setSidebarWidth] = useState(250) - const resizingRef = useRef(false) - const startXRef = useRef(0) - const startWidthRef = useRef(250) - const pollRef = useRef<ReturnType<typeof setInterval> | null>(null) - - // Sync URL param on mount - useEffect(() => { - if (urlDirectiveId && urlDirectiveId !== selectedId) { - setSelectedId(urlDirectiveId) - } - }, [urlDirectiveId]) - - // Handle directive selection - update URL - const handleSelectDirective = useCallback((id: string) => { - setSelectedId(id) - navigate(`/directives/${id}`, { replace: true }) - }, [navigate]) - - // Load directive when selected - useEffect(() => { - if (!selectedId) { - setDirective(null) - return - } - - let cancelled = false - async function load() { - try { - setLoading(true) - setError(null) - const data = await getDirective(selectedId!) - if (!cancelled) setDirective(data) - } catch (err) { - if (!cancelled) { - const msg = err instanceof Error ? err.message : 'Failed to load directive' - setError(msg) - addToast(msg, 'error') - } - } finally { - if (!cancelled) setLoading(false) - } - } - load() - - return () => { cancelled = true } - }, [selectedId, addToast]) - - // Step polling (after goal update triggers supervisor) - const startStepPolling = useCallback(() => { - if (pollRef.current) clearInterval(pollRef.current) - pollRef.current = setInterval(async () => { - if (!selectedId) return - try { - const data = await getDirective(selectedId) - setDirective(data) - } catch { - // Silently fail for polling - } - }, 3000) - // Stop after 60 seconds - setTimeout(() => { - if (pollRef.current) { - clearInterval(pollRef.current) - pollRef.current = null - } - }, 60000) - }, [selectedId]) - - useEffect(() => { - return () => { - if (pollRef.current) clearInterval(pollRef.current) - } - }, []) - - // Auto-save goal changes - const handleGoalChange = useCallback(async (newGoal: string) => { - if (!selectedId) return - try { - const updated = await updateGoal(selectedId, newGoal) - setDirective(updated) - addToast('Goal saved', 'success') - startStepPolling() - } catch (err) { - addToast(`Failed to save goal: ${(err as Error).message}`, 'error') - } - }, [selectedId, addToast, startStepPolling]) - - const handleTitleChange = useCallback(async (newTitle: string) => { - if (!selectedId || !directive) return - try { - const updated = await updateDirective(selectedId, { - title: newTitle, - version: directive.version, - }) - setDirective(updated) - } catch (err) { - addToast(`Failed to update title: ${(err as Error).message}`, 'error') - } - }, [selectedId, directive, addToast]) - - const handleCleanup = useCallback(async () => { - if (!selectedId) return - try { - await cleanupDirective(selectedId) - addToast('Cleanup contract spawned', 'success') - startStepPolling() - } catch (err) { - addToast(`Cleanup failed: ${(err as Error).message}`, 'error') - } - }, [selectedId, addToast, startStepPolling]) - - const handleCreatePr = useCallback(async () => { - if (!selectedId) return - try { - await createPr(selectedId) - addToast('PR update triggered', 'success') - } catch (err) { - addToast(`PR update failed: ${(err as Error).message}`, 'error') - } - }, [selectedId, addToast]) - - const handlePlanOrders = useCallback(async () => { - if (!selectedId) return - try { - await pickUpOrders(selectedId) - addToast('Planning orders...', 'info') - startStepPolling() - } catch (err) { - addToast(`Plan orders failed: ${(err as Error).message}`, 'error') - } - }, [selectedId, addToast, startStepPolling]) - - const handleTogglePause = useCallback(async () => { - if (!selectedId || !directive) return - try { - if (directive.status === 'paused') { - const result = await startDirective(selectedId) - setDirective(result) - addToast('Directive resumed', 'success') - } else { - const updated = await pauseDirective(selectedId) - setDirective(updated) - addToast('Directive paused', 'info') - } - } catch (err) { - addToast(`Failed to toggle pause: ${(err as Error).message}`, 'error') - } - }, [selectedId, directive, addToast]) - - // Sidebar resize handlers - const handleMouseDown = useCallback((e: React.MouseEvent) => { - resizingRef.current = true - startXRef.current = e.clientX - startWidthRef.current = sidebarWidth - document.body.style.cursor = 'col-resize' - document.body.style.userSelect = 'none' - - const handleMouseMove = (e: MouseEvent) => { - if (!resizingRef.current) return - const diff = e.clientX - startXRef.current - const newWidth = Math.max(180, Math.min(500, startWidthRef.current + diff)) - setSidebarWidth(newWidth) - } - - const handleMouseUp = () => { - resizingRef.current = false - document.body.style.cursor = '' - document.body.style.userSelect = '' - document.removeEventListener('mousemove', handleMouseMove) - document.removeEventListener('mouseup', handleMouseUp) - } - - document.addEventListener('mousemove', handleMouseMove) - document.addEventListener('mouseup', handleMouseUp) - }, [sidebarWidth]) - - const handleNewDirective = useCallback(() => { - // Placeholder - will be implemented with full directive creation flow - console.log('New directive requested') - }, []) - - return ( - <div className="document-layout"> - {/* Sidebar */} - <div className="document-sidebar" style={{ width: sidebarWidth }}> - <div className="document-sidebar-back"> - <Link to="/" className="document-back-link"> - {'\u2190'} Back to Main - </Link> - </div> - <DirectiveFileTree - selectedDirectiveId={selectedId} - onSelectDirective={handleSelectDirective} - onNewDirective={handleNewDirective} - /> - </div> - - {/* Resize handle */} - <div className="document-resize-handle" onMouseDown={handleMouseDown} /> - - {/* Main content */} - <div className="document-main"> - {directive && ( - <div className="document-topbar"> - <div className="document-topbar-left"> - <h1 className="document-topbar-title">{directive.title || 'Untitled'}</h1> - <StatusBadge status={directive.status} /> - </div> - <div className="document-topbar-right"> - <button className="document-topbar-gear" title="Settings"> - {'\u2699'} - </button> - </div> - </div> - )} - - <div className="document-content"> - {loading && ( - <div className="document-placeholder"> - <p>Loading directive...</p> - </div> - )} - - {error && ( - <div className="document-placeholder"> - <p className="document-error">Error: {error}</p> - </div> - )} - - {!loading && !error && !directive && ( - <div className="document-placeholder"> - <div className="document-placeholder-icon">{'\u{1F4DD}'}</div> - <h2>No directive selected</h2> - <p>Select a directive from the sidebar or create a new one to get started.</p> - </div> - )} - - {!loading && !error && directive && ( - <DocumentEditor - directiveId={directive.id} - title={directive.title || 'Untitled'} - goal={directive.goal || ''} - status={directive.status} - prBranch={directive.prBranch || directive.pr_branch} - onGoalChange={handleGoalChange} - onTitleChange={handleTitleChange} - onCleanup={handleCleanup} - onCreatePr={handleCreatePr} - onPlanOrders={handlePlanOrders} - onTogglePause={handleTogglePause} - /> - )} - </div> - </div> - </div> - ) -} - -// Wrapper that provides toast context -export default function DocumentLayout() { - return ( - <ToastProvider> - <DocumentLayoutInner /> - </ToastProvider> - ) -} |
