diff --git a/apps/web/src/components/ai/openclaw-panel.tsx b/apps/web/src/components/ai/openclaw-panel.tsx index 8feb010c..e72a3426 100644 --- a/apps/web/src/components/ai/openclaw-panel.tsx +++ b/apps/web/src/components/ai/openclaw-panel.tsx @@ -192,9 +192,95 @@ export function OpenClawPanel({ )} + + {/* Sprint 5R S6: 狀態數據 + AI Terminal */} +
+
+
+ 模型: openclaw_nemo +
+
● 運行中
+
+ + +
) } +/** AI 即時統計 */ +function OpenClawStats() { + const [stats, setStats] = useState<{ analyses: number; successRate: string; mttr: string }>({ analyses: 0, successRate: '--', mttr: '--' }) + const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? '' + + useEffect(() => { + Promise.all([ + fetch(`${API_BASE}/api/v1/stats/disposition`).then(r => r.ok ? r.json() : null).catch(() => null), + fetch(`${API_BASE}/api/v1/incidents`).then(r => r.ok ? r.json() : null).catch(() => null), + ]).then(([disp, inc]) => { + const total = disp?.summary?.total ?? 0 + const rate = disp?.summary?.auto_rate != null ? `${Math.round(disp.summary.auto_rate * 100)}%` : '--' + const incidents = inc?.incidents ?? inc ?? [] + const resolved = incidents.filter((i: any) => i.updated_at && (i.status === 'resolved' || i.status === 'closed')) + let mttr = '--' + if (resolved.length > 0) { + const avg = resolved.reduce((sum: number, i: any) => sum + (new Date(i.updated_at).getTime() - new Date(i.created_at).getTime()), 0) / resolved.length + mttr = `${(avg / 60000).toFixed(1)}m` + } + setStats({ analyses: total, successRate: rate, mttr }) + }) + }, [API_BASE]) + + return ( +
+ 今日分析: {stats.analyses} + 成功率: {stats.successRate} + MTTR: {stats.mttr} +
+ ) +} + +/** AI 推理終端 */ +function OpenClawTerminal() { + const [logs, setLogs] = useState([]) + const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? '' + + useEffect(() => { + fetch(`${API_BASE}/api/v1/alert-operation-logs?limit=4`) + .then(r => r.ok ? r.json() : { items: [] }) + .then(d => { + const items = (d.items ?? []).reverse() + setLogs(items.map((item: any) => { + const time = (() => { try { return new Date(item.created_at).toLocaleTimeString('zh-TW', { timeZone: 'Asia/Taipei', hour: '2-digit', minute: '2-digit' }) } catch { return '--:--' } })() + return `[${time}] ${item.action_detail || item.event_type.replace(/_/g, ' ')}` + })) + }) + .catch(() => {}) + }, [API_BASE]) + + return ( +
+ + {logs.length === 0 ? ( + [--:--] Scanning cluster state... + ) : ( + logs.map((line, i) => ( +
+ {i < logs.length - 1 ? ( + <>{line.slice(0, 7)}{line.slice(7)} + ) : ( + {line} + )} +
+ )) + )} +
+ ) +} + export default OpenClawPanel