summaryrefslogtreecommitdiff
path: root/frontend/src/components/document/DocumentLayout.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/document/DocumentLayout.tsx')
-rw-r--r--frontend/src/components/document/DocumentLayout.tsx316
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>
- )
-}