feat(agent-office): 모바일 반응형 세로 스택 + 작업 시간 표기 개선
- 768px 이하에서 대시보드 세로 스택 + 에이전트 카드 아코디언 토글 - waiting/알림 있을 때 자동 펼침 및 좌측 강조 바 - 픽셀 오피스 캔버스 모바일 높이 140px로 축소 후 상단 배치 - 최근 작업 시간: completed_at 우선 + 오늘/어제/MM-DD HH:MM 포맷 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,9 +35,12 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
const [input, setInput] = useState('');
|
||||
const [activeCommand, setActiveCommand] = useState(null);
|
||||
const [tokenUsage, setTokenUsage] = useState(null);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const state = agentState || { state: 'offline' };
|
||||
const commands = AGENT_COMMANDS[agentId] || [];
|
||||
const needsAttention = state.state === 'waiting' || notification > 0;
|
||||
const isOpen = expanded || needsAttention;
|
||||
|
||||
useEffect(() => {
|
||||
getAgentTasks(agentId, 10)
|
||||
@@ -89,11 +92,31 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
setActiveCommand(null);
|
||||
};
|
||||
|
||||
const formatTime = (t) => t ? t.replace('T', ' ').slice(11, 19) : '';
|
||||
const formatTaskTime = (task) => {
|
||||
const iso = task.completed_at || task.created_at;
|
||||
if (!iso) return '';
|
||||
const d = new Date(iso);
|
||||
if (isNaN(d.getTime())) return '';
|
||||
const now = new Date();
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
const hm = `${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
const sameDay = d.toDateString() === now.toDateString();
|
||||
const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1);
|
||||
const isYesterday = d.toDateString() === yesterday.toDateString();
|
||||
if (sameDay) return `오늘 ${hm}`;
|
||||
if (isYesterday) return `어제 ${hm}`;
|
||||
return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${hm}`;
|
||||
};
|
||||
|
||||
const handleHeaderClick = (e) => {
|
||||
e.stopPropagation();
|
||||
setExpanded(v => !v);
|
||||
onClearNotification();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ao-col" onClick={onClearNotification}>
|
||||
<div className="ao-col-header" style={{ borderColor: meta.color }}>
|
||||
<div className={`ao-col ${isOpen ? 'ao-col--open' : 'ao-col--collapsed'} ${needsAttention ? 'ao-col--attention' : ''}`} onClick={onClearNotification}>
|
||||
<div className="ao-col-header" style={{ borderColor: meta.color }} onClick={handleHeaderClick}>
|
||||
<span className="ao-col-name" style={{ color: meta.color }}>{meta.name}</span>
|
||||
{tokenUsage && tokenUsage.total_tokens > 0 && (
|
||||
<span
|
||||
@@ -105,8 +128,12 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
)}
|
||||
<span className={`ao-col-state ao-col-state--${state.state}`}>{state.state}</span>
|
||||
{notification > 0 && <span className="ao-col-badge">{notification}</span>}
|
||||
<span className="ao-col-chevron" aria-hidden="true">{isOpen ? '▾' : '▸'}</span>
|
||||
</div>
|
||||
|
||||
<div className="ao-col-body">
|
||||
|
||||
|
||||
{state.detail && (
|
||||
<div className="ao-col-detail">{state.detail}</div>
|
||||
)}
|
||||
@@ -153,7 +180,7 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
<span className="ao-col-task-badge" style={{ background: badge.bg }}>{badge.label}</span>
|
||||
</div>
|
||||
<div className="ao-col-task-time">
|
||||
{formatTime(task.created_at)}
|
||||
{formatTaskTime(task)}
|
||||
{task.result_data?.telegram_sent !== undefined && (
|
||||
<span className="ao-doc-tg-status">{task.result_data.telegram_sent ? ' TG OK' : ' TG Fail'}</span>
|
||||
)}
|
||||
@@ -168,6 +195,7 @@ const AgentColumn = ({ agentId, meta, agentState, notification, onCommand, onApp
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user