import { useState } from "react"; import { useConsole } from "../../context"; import { Bar, DataList } from "../../components/ui"; import { fmt, shortId } from "../../api/client"; import type { Automation, AutomationDetail } from "../../api/types"; const PROMPT_PREVIEW = 160; function humanInterval(s?: number): string { if (!s) return ""; if (s % 86400 === 0) return "(每天)"; if (s % 3600 === 0) return "(每小时)"; if (s % 60 === 0) return "(每 " + s / 60 + " 分钟)"; return ""; } // Automation detail (read-only display): backed by GET // /projects/{pid}/automations/{aid} — full definition + runs[] + // runs_note + heartbeat_binding. Editing is NOT duplicated here: // the single edit form is main's inline edit in the list below // (updateAutomation with the (a, name, prompt, interval, repo) // signature). Detail display + runs + task buttons are our side's // addition; cross-view jumps use location.hash because the IDE // shell switches views on hashchange (no sec-conv / sec-automations // element to scroll to). function AutomationDetailBox({ detail }: { detail: AutomationDetail }) { const ctx = useConsole(); const openTask = (id?: string) => { if (!id) return; ctx.showDetail(id); try { window.location.hash = "#view-tasks"; } catch { /* noop */ } }; return (

自动化详情:{detail.name}

状态 {detail.status}{detail.due ? "|到期" : ""} | 每 {detail.interval_seconds} 秒 | 下次 {fmt(detail.next_run_at)} | 上次执行 {fmt(detail.last_run_at)} | 创建人 {detail.created_by ? shortId(detail.created_by) : ""} | 创建于 {fmt(detail.created_at)} {detail.cancelled_at ? " | 已取消于 " + fmt(detail.cancelled_at) : ""} {detail.heartbeat_binding ? " | 心跳绑定:已绑定" : ""}
{detail.prompt || ""}
{detail.repo_url ?
仓库 {detail.repo_url}
: null} 编辑请在下面列表里点“编辑”。{ctx.autoDetailMsg}
最近产出({(detail.runs || []).length}) ( {r.status}{r.created_at ? "|" + fmt(r.created_at) : ""}{r.task_id ? <> | 任务 : ""}{r.detail ? "|" + String(r.detail).slice(0, 120) : ""} ))} />
{detail.runs_note ?
{detail.runs_note}
: null}
); } export function AutomationPanel() { const ctx = useConsole(); const [name, setName] = useState(""); const [prompt, setPrompt] = useState(""); const [interval, setInterval] = useState(""); const [repo, setRepo] = useState(""); const [editingId, setEditingId] = useState(""); const [editName, setEditName] = useState(""); const [editPrompt, setEditPrompt] = useState(""); const [editInterval, setEditInterval] = useState(""); const [editRepo, setEditRepo] = useState(""); const [expanded, setExpanded] = useState>({}); const startEdit = (a: Automation) => { setEditingId(a.id); setEditName(a.name || ""); setEditPrompt(a.prompt || ""); setEditInterval(a.interval_seconds ? String(a.interval_seconds) : ""); setEditRepo(a.repo_url || ""); }; return ( <> setName(e.target.value)} />