feat(agent-office): add CommandTab with quick actions, params, and approval UI

This commit is contained in:
2026-04-27 08:34:48 +09:00
parent 43904d033a
commit ce245609f9

View File

@@ -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 (
<div className="ao-command-tab">
{/* 승인 대기 UI */}
{agentState === 'waiting' && pendingTask && (
<div className="ao-approval-card">
<div className="ao-approval-title">Awaiting Approval</div>
<div className="ao-approval-desc">{pendingTask.task_type}: {pendingTask.detail || JSON.stringify(pendingTask.input_data)}</div>
<div className="ao-approval-actions">
<button className="ao-btn-approve" onClick={() => handleApproval(true)} disabled={loading}>Approve</button>
<button className="ao-btn-reject" onClick={() => handleApproval(false)} disabled={loading}>Reject</button>
</div>
</div>
)}
{/* Quick Actions */}
<div className="ao-section">
<div className="ao-section-label">Quick Actions</div>
<div className="ao-quick-actions">
{quickActions.map(qa => (
<button
key={qa.action}
className="ao-btn-quick"
onClick={() => handleQuickAction(qa.action)}
disabled={loading}
>
{qa.label}
</button>
))}
</div>
</div>
{/* Parameterized Action */}
{paramAction && (
<div className="ao-section">
<div className="ao-section-label">{paramAction.label}</div>
<div className="ao-param-row">
<input
className="ao-input"
value={paramInput}
onChange={e => setParamInput(e.target.value)}
placeholder={paramAction.placeholder}
onKeyDown={e => e.key === 'Enter' && handleParamAction()}
/>
<button className="ao-btn-send" onClick={handleParamAction} disabled={loading || !paramInput.trim()}>
Send
</button>
</div>
</div>
)}
{/* Custom Command */}
<div className="ao-section">
<div className="ao-section-label">Custom Command</div>
<input
className="ao-input"
value={customAction}
onChange={e => setCustomAction(e.target.value)}
placeholder="Action name"
/>
<input
className="ao-input"
value={customParams}
onChange={e => setCustomParams(e.target.value)}
placeholder='Parameters (JSON)'
style={{ marginTop: 4 }}
/>
<button
className="ao-btn-send"
onClick={handleCustomCommand}
disabled={loading || !customAction.trim()}
style={{ marginTop: 4, width: '100%' }}
>
Send Command
</button>
</div>
</div>
);
}