refactor(agent-office): dashboard layout with agent columns + CEO command panel

- Restructure layout: dashboard (top, 3 columns) + office canvas (bottom, 280px)
- AgentColumn: per-agent status, quick commands, approval UI, task history
- CommandColumn: CEO command input with agent selector, quick shortcuts, history
- Remove overlay panels (ChatPanel/DocumentPanel) - integrated into dashboard
- Office canvas shrunk to compact strip at bottom

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 15:32:07 +09:00
parent deb285695a
commit a165d6271f
4 changed files with 478 additions and 376 deletions

View File

@@ -1,27 +1,27 @@
import React, { useRef, useState, useCallback, useEffect } from 'react';
import { useAgentManager } from './hooks/useAgentManager';
import { useOfficeCanvas } from './hooks/useOfficeCanvas';
import ChatPanel from './components/ChatPanel';
import DocumentPanel from './components/DocumentPanel';
import AgentColumn from './components/AgentColumn';
import CommandColumn from './components/CommandColumn';
import './AgentOffice.css';
const AGENT_META = {
stock: { name: '주식 트레이더', color: '#4488cc' },
music: { name: '음악 프로듀서', color: '#44aa88' },
};
export function Component() {
const canvasContainerRef = useRef(null);
const [selectedAgent, setSelectedAgent] = useState(null);
const [showDocument, setShowDocument] = useState(false);
const { agents, pendingTasks, connected, notifications, sendCommand, sendApproval, clearNotifications } = useAgentManager();
const handleAgentClick = useCallback((agentId) => {
setSelectedAgent(prev => prev === agentId ? null : agentId);
clearNotifications(agentId);
}, [clearNotifications]);
const handleCeoClick = useCallback(() => {
setShowDocument(prev => !prev);
}, []);
const handleCeoClick = useCallback(() => {}, []);
const { updateAgentState, moveAgent, setAgentNotification, setCeoDocBadge } = useOfficeCanvas(canvasContainerRef, handleAgentClick, handleCeoClick);
const { updateAgentState, setAgentNotification, setCeoDocBadge } = useOfficeCanvas(canvasContainerRef, handleAgentClick, handleCeoClick);
useEffect(() => {
for (const [id, info] of Object.entries(agents)) {
@@ -53,41 +53,27 @@ export function Component() {
</div>
</div>
<div className="ao-workspace">
<div className="ao-canvas-container" ref={canvasContainerRef} />
<div className="ao-agent-bar">
{Object.entries(agents).map(([id, info]) => (
<button
key={id}
className={`ao-agent-chip ${info.state === 'waiting' ? 'ao-agent-chip--alert' : ''} ${selectedAgent === id ? 'ao-agent-chip--selected' : ''}`}
onClick={() => handleAgentClick(id)}
>
<span className={`ao-chip-dot ao-chip-dot--${info.state}`} />
{id}
{notifications[id] > 0 && (
<span className="ao-chip-badge">{notifications[id]}</span>
)}
</button>
))}
{pendingTasks.length > 0 && (
<span className="ao-pending-count">{pendingTasks.length} pending</span>
)}
</div>
{selectedAgent && (
<ChatPanel
agentId={selectedAgent}
agentState={agents[selectedAgent]}
<div className="ao-dashboard">
{['stock', 'music'].map(id => (
<AgentColumn
key={id}
agentId={id}
meta={AGENT_META[id]}
agentState={agents[id]}
notification={notifications[id] || 0}
onCommand={sendCommand}
onApproval={sendApproval}
onClose={() => setSelectedAgent(null)}
onClearNotification={() => clearNotifications(id)}
/>
)}
))}
<CommandColumn
agents={agents}
onCommand={sendCommand}
/>
</div>
{showDocument && (
<DocumentPanel onClose={() => setShowDocument(false)} />
)}
<div className="ao-office-section">
<div className="ao-canvas-container" ref={canvasContainerRef} />
</div>
</div>
);