import { useCallback, useEffect, useState } from "react"; import { CalendarClock, ClipboardList, LoaderCircle, Pencil, Plus, RefreshCw, Sparkles, Trash2, X } from "lucide-react"; import { Button } from "@/shared/ui/button"; import { DateTimeField } from "@/shared/ui/date-time-field"; import { Disclosure } from "@/shared/ui/disclosure"; import { FieldSelect } from "@/shared/ui/field-select"; import { Input } from "@/shared/ui/input"; import { StatusBadge, type StatusTone } from "@/shared/ui/status"; import { Textarea } from "@/shared/ui/textarea"; import { WorkOrderSubtasksPanel } from "../components/scheduled-condition-detail-panel"; import { WorkbenchEmptyState, WorkbenchNotice, WorkbenchSection } from "../components/workbench-panel-ui"; import type { ScheduledWorkOrderItem } from "../types"; import type { PeripheralContext, PeripheralSampleClient, PeripheralWorkOrder } from "./client"; type PeripheralSubtask = NonNullable[number]; type PeripheralStage = PeripheralSubtask["stages"][number]; export function ManagedTestResources({ client, canWrite, capabilities, refreshGeneration, onAgentTrigger }: { client: PeripheralSampleClient; canWrite: boolean; capabilities: PeripheralContext["managed_resources"]; refreshGeneration?: number; onAgentTrigger?: (workOrder: PeripheralWorkOrder) => Promise; }) { const [orders, setOrders] = useState([]); const [busy, setBusy] = useState(null); const [error, setError] = useState(null); const refresh = useCallback(async () => { if (capabilities.work_orders !== "available") return; setBusy("refresh"); setError(null); try { setOrders((await client.listWorkOrders()).items); } catch (nextError) { setError(message(nextError)); } finally { setBusy(null); } }, [capabilities.work_orders, client]); useEffect(() => { void refresh(); }, [refresh, refreshGeneration]); const mutate = async (key: string, operation: () => Promise) => { setBusy(key); setError(null); try { await operation(); await refresh(); } catch (nextError) { setError(message(nextError)); setBusy(null); } }; return ( } tone="teal" bodyClassName="space-y-3" actions={( )} > {error ? {error} : null} {capabilities.work_orders !== "available" ? ( 缺少 peripheral_sample.work_orders 或 work_order_revisions。 ) : ( )} ); } function WorkOrders({ orders, canWrite, busy, mutate, client, onAgentTrigger }: { orders: PeripheralWorkOrder[]; canWrite: boolean; busy: string | null; mutate: (key: string, operation: () => Promise) => Promise; client: PeripheralSampleClient; onAgentTrigger?: (workOrder: PeripheralWorkOrder) => Promise; }) { const [editorOpen, setEditorOpen] = useState(false); const [editing, setEditing] = useState(null); const [title, setTitle] = useState(""); const [summary, setSummary] = useState(""); const [status, setStatus] = useState("draft"); const [priority, setPriority] = useState("normal"); const [assignee, setAssignee] = useState(""); const [location, setLocation] = useState(""); const [scenarioCode, setScenarioCode] = useState(""); const [scheduledAt, setScheduledAt] = useState(() => toLocalInput(new Date(Date.now() + 60 * 60_000))); const [subtasks, setSubtasks] = useState(null); const [eventEditorOpen, setEventEditorOpen] = useState(false); const [eventKind, setEventKind] = useState<"water_quality_complaint" | "burst_report">("water_quality_complaint"); const [eventLocation, setEventLocation] = useState(""); const [eventOccurredAt, setEventOccurredAt] = useState(toLocalInput(new Date())); const [eventDescription, setEventDescription] = useState(""); const [agentTriggerFailure, setAgentTriggerFailure] = useState<{ message: string; workOrder: PeripheralWorkOrder } | null>(null); const clear = () => { setEditing(null); setEditorOpen(false); setTitle(""); setSummary(""); setStatus("draft"); setPriority("normal"); setAssignee(""); setLocation(""); setScenarioCode(""); setScheduledAt(toLocalInput(new Date(Date.now() + 60 * 60_000))); setSubtasks(null); }; const create = () => { clear(); setEditorOpen(true); setSubtasks([createDefaultSubtask()]); }; const edit = (item: PeripheralWorkOrder) => { setEditing(item); setEditorOpen(true); setTitle(item.title); setSummary(item.summary); setStatus(item.status); setPriority(item.priority); setAssignee(item.assignee ?? ""); setLocation(item.location ?? ""); setScenarioCode(item.scenario_code ?? ""); setScheduledAt(item.scheduled_at ? toLocalInput(new Date(item.scheduled_at)) : ""); setSubtasks(item.subtasks ?? null); }; const triggerAgent = async (workOrder: PeripheralWorkOrder) => { if (!onAgentTrigger) { setAgentTriggerFailure({ message: "当前工作台未连接 Agent 事件入口。", workOrder }); return; } try { await onAgentTrigger(workOrder); setAgentTriggerFailure(null); } catch (error) { setAgentTriggerFailure({ message: message(error), workOrder }); } }; const createEventWorkOrder = () => void mutate("create-event-order", async () => { const waterQuality = eventKind === "water_quality_complaint"; const workOrder = await client.createWorkOrder({ title: waterQuality ? `水质投诉:${eventLocation.trim()}` : `爆管上报:${eventLocation.trim()}`, summary: eventDescription.trim(), status: "open", priority: waterQuality ? "high" : "urgent", assignee: null, scheduled_at: new Date(eventOccurredAt).toISOString(), work_order_type: eventKind, location: eventLocation.trim(), scenario_code: waterQuality ? "WQ-SECONDARY-SUPPLY-001" : "BURST-REPORT-001", subtasks: null }); setEventEditorOpen(false); setEventLocation(""); setEventDescription(""); await triggerAgent(workOrder); }); const processValid = Boolean(subtasks?.length && subtasks.every(isValidSubtask)); const canSave = canWrite && !busy && title.trim() && summary.trim() && processValid; return (

测试工单

过程字段将随工单写入,并按原结构同步到预约任务。

{eventEditorOpen ? (
setEventEditorOpen(false)} />
setEventKind(value as typeof eventKind)} disabled={!canWrite || Boolean(busy)} options={[{ value: "water_quality_complaint", label: "水质投诉" }, { value: "burst_report", label: "爆管上报" }]} />