From 7ff8dc70a2086faba4364ccc064c95253f1857c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 22 Mar 2026 02:01:33 -0400 Subject: [PATCH] refactor(01-02): decompose execution.tsx and automation.tsx into focused sub-components - execution.tsx (1749 lines) -> 16 focused files under 300 lines each: TaskQueue, TaskInspector, TaskCreator, TaskComments, ApprovalList, ApprovalInspector, ActivityList, CostList, BudgetSummary, WorkspaceList, WorkspaceInspector, WorkspaceCreator, SecretCreator, SettingsPane, PendingReviewList, execution-utils - automation.tsx (1697 lines) -> 6 focused files under 300 lines each: AutomationPanel, RuleCard, RuleEditor, WorkflowList, WorkflowEditor, automation-config - Both original files replaced with backward-compatible re-exports - All sub-components under 300 lines, TypeScript compilation passes --- src/renderer/components/automation.tsx | 1703 +--------------- .../components/automation/AutomationPanel.tsx | 96 + .../components/automation/RuleCard.tsx | 35 + .../components/automation/RuleEditor.tsx | 51 + .../components/automation/WorkflowEditor.tsx | 45 + .../components/automation/WorkflowList.tsx | 59 + .../automation/automation-config.ts | 72 + src/renderer/components/execution.tsx | 1764 +---------------- .../components/execution/ActivityList.tsx | 51 + .../execution/ApprovalInspector.tsx | 61 + .../components/execution/ApprovalList.tsx | 21 + .../components/execution/BudgetSummary.tsx | 47 + .../components/execution/CostList.tsx | 38 + .../execution/PendingReviewList.tsx | 27 + .../components/execution/SecretCreator.tsx | 31 + .../components/execution/SettingsPane.tsx | 45 + .../components/execution/TaskComments.tsx | 60 + .../components/execution/TaskCreator.tsx | 36 + .../components/execution/TaskInspector.tsx | 96 + .../components/execution/TaskQueue.tsx | 28 + .../components/execution/WorkspaceCreator.tsx | 28 + .../execution/WorkspaceInspector.tsx | 33 + .../components/execution/WorkspaceList.tsx | 48 + .../components/execution/execution-utils.tsx | 46 + 24 files changed, 1075 insertions(+), 3446 deletions(-) create mode 100644 src/renderer/components/automation/AutomationPanel.tsx create mode 100644 src/renderer/components/automation/RuleCard.tsx create mode 100644 src/renderer/components/automation/RuleEditor.tsx create mode 100644 src/renderer/components/automation/WorkflowEditor.tsx create mode 100644 src/renderer/components/automation/WorkflowList.tsx create mode 100644 src/renderer/components/automation/automation-config.ts create mode 100644 src/renderer/components/execution/ActivityList.tsx create mode 100644 src/renderer/components/execution/ApprovalInspector.tsx create mode 100644 src/renderer/components/execution/ApprovalList.tsx create mode 100644 src/renderer/components/execution/BudgetSummary.tsx create mode 100644 src/renderer/components/execution/CostList.tsx create mode 100644 src/renderer/components/execution/PendingReviewList.tsx create mode 100644 src/renderer/components/execution/SecretCreator.tsx create mode 100644 src/renderer/components/execution/SettingsPane.tsx create mode 100644 src/renderer/components/execution/TaskComments.tsx create mode 100644 src/renderer/components/execution/TaskCreator.tsx create mode 100644 src/renderer/components/execution/TaskInspector.tsx create mode 100644 src/renderer/components/execution/TaskQueue.tsx create mode 100644 src/renderer/components/execution/WorkspaceCreator.tsx create mode 100644 src/renderer/components/execution/WorkspaceInspector.tsx create mode 100644 src/renderer/components/execution/WorkspaceList.tsx create mode 100644 src/renderer/components/execution/execution-utils.tsx diff --git a/src/renderer/components/automation.tsx b/src/renderer/components/automation.tsx index e160ead9..18718ad8 100644 --- a/src/renderer/components/automation.tsx +++ b/src/renderer/components/automation.tsx @@ -1,1697 +1,6 @@ -import { useCallback, useEffect, useMemo, useState } from "react" -import { motion, AnimatePresence } from "framer-motion" -import { - ArrowRight, - ArrowsClockwise, - CaretRight, - Eye, - Funnel, - Gear, - Lightning, - Pause, - Play, - Plus, - Robot, - Timer, - Trash, - X, -} from "@phosphor-icons/react" -import type { - AutomationAction, - AutomationRuleRecord, - AutomationStatus, - AutomationTrigger, - DepartmentId, - WorkflowPipelineRecord, - WorkflowStatus, - WorkflowStep, -} from "@shared/types" -import { timeAgo } from "../lib/formatters" -import { DEPARTMENT_OPTIONS } from "@shared/constants" -import { ActionButton, Select } from "./ui" -import { useT } from "../i18n" - -const ACCENT = "var(--accent)" -const LINE = "var(--line)" - -const triggerConfig: Record = { - task_status_changed: { label: "Task Status Changed", description: "When a task transitions to a new status", icon: ArrowsClockwise }, - task_created: { label: "Task Created", description: "When a new task is added to the system", icon: Plus }, - task_blocked: { label: "Task Blocked", description: "When a task becomes blocked by a dependency", icon: Pause }, - task_reassigned: { label: "Task Reassigned", description: "When a task is reassigned to a different agent", icon: ArrowsClockwise }, - run_completed: { label: "Run Completed", description: "When an agent run finishes successfully", icon: Play }, - run_failed: { label: "Run Failed", description: "When an agent run encounters an error", icon: Trash }, - approval_created: { label: "Approval Created", description: "When a new approval request needs review", icon: Eye }, - approval_resolved: { label: "Approval Resolved", description: "When a pending approval is decided", icon: Eye }, - budget_threshold: { label: "Budget Threshold", description: "When spending exceeds a configured limit", icon: Funnel }, - agent_idle: { label: "Agent Idle", description: "When an agent has no tasks and becomes idle", icon: Robot }, - comment_posted: { label: "Comment Posted", description: "When a new comment is added to a task", icon: Lightning }, - document_created: { label: "Document Created", description: "When a new document is generated", icon: Lightning }, - schedule: { label: "Scheduled", description: "Runs on a cron-like schedule automatically", icon: Timer }, - hire_approved: { label: "Hire Approved", description: "When a new agent hire is approved", icon: Robot }, - sprint_started: { label: "Sprint Started", description: "When a sprint transitions to active", icon: Play }, - sprint_ended: { label: "Sprint Ended", description: "When a sprint reaches completion", icon: ArrowsClockwise }, -} - -const actionConfig: Record = { - assign_task: { label: "Assign Task", description: "Auto-assign a task to the best available agent" }, - create_task: { label: "Create Task", description: "Generate a new task from a template" }, - notify_agent: { label: "Notify Agent", description: "Send a notification to a specific agent" }, - send_message: { label: "Send Message", description: "Deliver a message via inter-agent messaging" }, - trigger_heartbeat: { label: "Trigger Heartbeat", description: "Wake an agent and trigger their heartbeat loop" }, - create_approval: { label: "Create Approval", description: "Request human approval before proceeding" }, - update_task_status: { label: "Update Task Status", description: "Move a task to a new status column" }, - escalate_to_manager: { label: "Escalate to Manager", description: "Route to the responsible manager for review" }, - cross_department_notify: { label: "Cross-Dept Notify", description: "Alert another department about this event" }, - schedule_meeting: { label: "Schedule Meeting", description: "Create a meeting with relevant participants" }, - create_document: { label: "Create Document", description: "Generate a document from a template" }, - auto_approve: { label: "Auto Approve", description: "Automatically approve matching requests" }, - reassign_task: { label: "Reassign Task", description: "Move task to a different agent" }, - run_task: { label: "Run Task", description: "Immediately execute the task with an agent" }, -} - -const departmentOptions = DEPARTMENT_OPTIONS - -const statusColors: Record = { - active: { bg: "var(--success-soft)", text: "var(--success)", dot: "var(--success)" }, - paused: { bg: "var(--warn-soft)", text: "var(--warn)", dot: "var(--warn)" }, - disabled: { bg: "var(--panel-soft)", text: "var(--muted)", dot: "var(--muted)" }, - error: { bg: "var(--danger-soft)", text: "var(--danger)", dot: "var(--danger)" }, -} - -const workflowStatusColors: Record = { - draft: { bg: "var(--panel-soft)", text: "var(--muted-strong)", dot: "var(--muted)" }, - active: { bg: "var(--success-soft)", text: "var(--success)", dot: "var(--success)" }, - paused: { bg: "var(--warn-soft)", text: "var(--warn)", dot: "var(--warn)" }, - completed: { bg: "var(--success-soft)", text: "var(--success)", dot: "var(--success)" }, - failed: { bg: "var(--danger-soft)", text: "var(--danger)", dot: "var(--danger)" }, -} - -const stepStatusConfig: Record = { - pending: { bg: "var(--panel-soft)", border: "var(--line)", text: "var(--muted)" }, - running: { bg: "var(--accent-soft)", border: ACCENT, text: ACCENT }, - completed: { bg: "var(--success-soft)", border: "var(--success)", text: "var(--success)" }, - failed: { bg: "var(--danger-soft)", border: "var(--danger)", text: "var(--danger)" }, - skipped: { bg: "var(--panel-soft)", border: "var(--line)", text: "var(--muted)" }, -} - -function safeJsonParse(json: string): unknown { - try { - return JSON.parse(json) - } catch { - return {} - } -} - -function safeJsonStringify(value: string): string { - try { - const parsed = JSON.parse(value) - return JSON.stringify(parsed, null, 2) - } catch { - return value - } -} - -interface AutomationLogEntry { - ruleId: string - ruleName: string - trigger: string - action: string - executedAt: string -} - -function RuleCard({ - rule, - onToggle, - onDelete, - onEdit, -}: { - rule: AutomationRuleRecord - onToggle: () => void - onDelete: () => void - onEdit: () => void -}) { - const triggerInfo = triggerConfig[rule.trigger] - const actionInfo = actionConfig[rule.action] - const TriggerIcon = triggerInfo?.icon ?? Lightning - const colors = statusColors[rule.status] - - return ( - -
-
- -
- - - -
- - -
-
-
- ) -} - -interface RuleFormState { - name: string - description: string - trigger: AutomationTrigger - conditionsJson: string - action: AutomationAction - actionConfigJson: string - sourceDepartment: string - targetDepartment: string - priority: number -} - -const defaultRuleForm: RuleFormState = { - name: "", - description: "", - trigger: "task_status_changed", - conditionsJson: "{}", - action: "assign_task", - actionConfigJson: "{}", - sourceDepartment: "", - targetDepartment: "", - priority: 50, -} - -function ruleToFormState(rule: AutomationRuleRecord): RuleFormState { - return { - name: rule.name, - description: rule.description, - trigger: rule.trigger, - conditionsJson: safeJsonStringify(rule.conditionsJson), - action: rule.action, - actionConfigJson: safeJsonStringify(rule.actionConfigJson), - sourceDepartment: rule.sourceDepartment ?? "", - targetDepartment: rule.targetDepartment ?? "", - priority: rule.priority, - } -} - -function RuleFormModal({ - open, - editingRule, - companyId, - onClose, - onSaved, -}: { - open: boolean - editingRule: AutomationRuleRecord | null - companyId: string - onClose: () => void - onSaved: () => void -}) { - const isEditing = editingRule !== null - const [form, setForm] = useState(defaultRuleForm) - const [saving, setSaving] = useState(false) - const [error, setError] = useState(null) - - useEffect(() => { - if (open) { - setForm(editingRule ? ruleToFormState(editingRule) : defaultRuleForm) - setError(null) - } - }, [open, editingRule]) - - const updateField = useCallback((key: K, value: RuleFormState[K]) => { - setForm((prev) => ({ ...prev, [key]: value })) - }, []) - - const handleSave = useCallback(async () => { - if (!form.name.trim()) { - setError("Rule name is required") - return - } - try { JSON.parse(form.conditionsJson) } catch { - setError("Conditions must be valid JSON") - return - } - try { JSON.parse(form.actionConfigJson) } catch { - setError("Action config must be valid JSON") - return - } - - setSaving(true) - setError(null) - try { - const result = await window.agentCompany.saveAutomationRule({ - id: editingRule?.id ?? null, - companyId, - name: form.name.trim(), - description: form.description.trim(), - trigger: form.trigger, - conditionsJson: form.conditionsJson, - action: form.action, - actionConfigJson: form.actionConfigJson, - sourceDepartment: (form.sourceDepartment || null) as DepartmentId | null, - targetDepartment: (form.targetDepartment || null) as DepartmentId | null, - priority: form.priority, - status: editingRule?.status ?? "active", - }) - if (!result.ok) { - setError(result.error.message) - return - } - onSaved() - onClose() - } catch (err) { - setError(err instanceof Error ? err.message : "Failed to save rule") - } finally { - setSaving(false) - } - }, [form, editingRule, companyId, onSaved, onClose]) - - if (!open) return null - - const triggerOptions = Object.entries(triggerConfig).map(([key, config]) => ({ - value: key, - label: config.label, - })) - - const actionOptions = Object.entries(actionConfig).map(([key, config]) => ({ - value: key, - label: config.label, - })) - - const selectedTriggerInfo = triggerConfig[form.trigger] - const selectedActionInfo = actionConfig[form.action] - - return ( - - e.stopPropagation()} - > -
-
-
- -
-

- {isEditing ? "Edit Automation Rule" : "New Automation Rule"} -

-
- -
- -
-
- {error ? ( -
- {error} -
- ) : null} - -
- - updateField("name", e.target.value)} - placeholder="e.g., Auto-assign blocked tasks" - className="focus-ring w-full rounded-[8px] border bg-[color:var(--panel)] px-3.5 py-2.5 text-[14px] text-[color:var(--text)] placeholder:text-[color:var(--muted)]" - style={{ borderColor: LINE }} - /> -
- -
- -