From ce245609f9104cfaf5ae237b2fd39802a045e0d8 Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 27 Apr 2026 08:34:48 +0900 Subject: [PATCH] feat(agent-office): add CommandTab with quick actions, params, and approval UI --- .../agent-office/components/CommandTab.jsx | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 src/pages/agent-office/components/CommandTab.jsx diff --git a/src/pages/agent-office/components/CommandTab.jsx b/src/pages/agent-office/components/CommandTab.jsx new file mode 100644 index 0000000..4e6806d --- /dev/null +++ b/src/pages/agent-office/components/CommandTab.jsx @@ -0,0 +1,164 @@ +// src/pages/agent-office/components/CommandTab.jsx +import { useState } from 'react'; +import { sendAgentCommand, approveAgentTask } from '../../../api'; + +const QUICK_ACTIONS = { + stock: [{ action: 'fetch_news', label: 'Fetch News' }, { action: 'test_telegram', label: 'Test Telegram' }], + music: [{ action: 'credits', label: 'Check Credits' }], + blog: [{ action: 'list_trend_keywords', label: 'List Keywords' }], + realestate: [{ action: 'dashboard', label: 'Dashboard' }], + lotto: [{ action: 'status', label: 'Status' }, { action: 'curate_now', label: 'Curate Now' }] +}; + +const PARAM_ACTIONS = { + stock: { action: 'add_alert', label: 'Add Alert', placeholder: '{"symbol":"005930","target_price":70000,"direction":"above"}' }, + music: { action: 'compose', label: 'Compose', placeholder: 'jazzy lo-fi piano beat' }, + blog: { action: 'research', label: 'Research', placeholder: 'keyword to research' }, + realestate: { action: 'fetch_matches', label: 'Fetch Matches', placeholder: '' }, + lotto: null +}; + +export default function CommandTab({ agentId, agentState, pendingTask, onCommandResult }) { + const [customAction, setCustomAction] = useState(''); + const [customParams, setCustomParams] = useState(''); + const [paramInput, setParamInput] = useState(''); + const [loading, setLoading] = useState(false); + + const quickActions = QUICK_ACTIONS[agentId] || []; + const paramAction = PARAM_ACTIONS[agentId]; + + const handleQuickAction = async (action) => { + setLoading(true); + try { + const result = await sendAgentCommand(agentId, action, {}); + onCommandResult?.(result); + } finally { + setLoading(false); + } + }; + + const handleParamAction = async () => { + if (!paramAction || !paramInput.trim()) return; + setLoading(true); + try { + let params = {}; + if (paramAction.action === 'compose') { + params = { prompt: paramInput }; + } else if (paramAction.action === 'research') { + params = { keyword: paramInput }; + } else { + try { params = JSON.parse(paramInput); } catch { params = { value: paramInput }; } + } + const result = await sendAgentCommand(agentId, paramAction.action, params); + onCommandResult?.(result); + setParamInput(''); + } finally { + setLoading(false); + } + }; + + const handleCustomCommand = async () => { + if (!customAction.trim()) return; + setLoading(true); + try { + let params = {}; + if (customParams.trim()) { + try { params = JSON.parse(customParams); } catch { params = { value: customParams }; } + } + const result = await sendAgentCommand(agentId, customAction, params); + onCommandResult?.(result); + setCustomAction(''); + setCustomParams(''); + } finally { + setLoading(false); + } + }; + + const handleApproval = async (approved) => { + if (!pendingTask) return; + setLoading(true); + try { + await approveAgentTask(agentId, pendingTask.id, approved); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* 승인 대기 UI */} + {agentState === 'waiting' && pendingTask && ( +
+
Awaiting Approval
+
{pendingTask.task_type}: {pendingTask.detail || JSON.stringify(pendingTask.input_data)}
+
+ + +
+
+ )} + + {/* Quick Actions */} +
+
Quick Actions
+
+ {quickActions.map(qa => ( + + ))} +
+
+ + {/* Parameterized Action */} + {paramAction && ( +
+
{paramAction.label}
+
+ setParamInput(e.target.value)} + placeholder={paramAction.placeholder} + onKeyDown={e => e.key === 'Enter' && handleParamAction()} + /> + +
+
+ )} + + {/* Custom Command */} +
+
Custom Command
+ setCustomAction(e.target.value)} + placeholder="Action name" + /> + setCustomParams(e.target.value)} + placeholder='Parameters (JSON)' + style={{ marginTop: 4 }} + /> + +
+
+ ); +}