135 lines
6.5 KiB
JavaScript
135 lines
6.5 KiB
JavaScript
// DetailPanel — tabs: overview, progress (activity + log surface)
|
|
function Tabs({ tab, onTab }) {
|
|
const items = [
|
|
{ id: 'overview', label: '概要' },
|
|
{ id: 'progress', label: '進捗' },
|
|
{ id: 'subtasks', label: 'サブタスク' },
|
|
];
|
|
return (
|
|
<div style={{ display: 'flex', gap: 4, padding: '8px 12px', borderBottom: '1px solid #e2e8f0', background: '#fff' }}>
|
|
{items.map(it => (
|
|
<button key={it.id} onClick={() => onTab(it.id)} style={{
|
|
padding: '6px 12px', borderRadius: 8, fontSize: 12, fontWeight: 600,
|
|
border: 'none', cursor: 'pointer', fontFamily: 'inherit',
|
|
background: tab === it.id ? '#eff6ff' : 'transparent',
|
|
color: tab === it.id ? '#1d4ed8' : '#64748b',
|
|
}}>{it.label}</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function OverviewTab({ task }) {
|
|
const Row = ({ label, value }) => (
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, padding: '8px 0', borderBottom: '1px solid #f1f5f9', fontSize: 12 }}>
|
|
<span style={{ color: '#64748b' }}>{label}</span>
|
|
<span style={{ color: '#0f172a', fontWeight: 600, textAlign: 'right' }}>{value}</span>
|
|
</div>
|
|
);
|
|
return (
|
|
<div style={{ padding: 16, overflowY: 'auto', fontSize: 13, color: '#0f172a' }}>
|
|
<div style={{ fontSize: 10, fontFamily: 'IBM Plex Mono, monospace', color: '#94a3b8', letterSpacing: '.08em' }}>DESCRIPTION</div>
|
|
<div style={{ marginTop: 6, color: '#334155', lineHeight: 1.6, fontSize: 13 }}>{task.body}</div>
|
|
|
|
<div style={{ marginTop: 16, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
|
<StatChip label="試行" value={`${task.attempts}/3`} />
|
|
<StatChip label="ピース" value={task.piece} color="#2563eb" />
|
|
<StatChip label="ワーカー" value={task.worker || '—'} />
|
|
</div>
|
|
|
|
<div style={{ marginTop: 16 }}>
|
|
<Row label="リポジトリ" value={<span style={{ fontFamily: 'IBM Plex Mono, monospace', fontSize: 11 }}>{task.repo}</span>} />
|
|
<Row label="ブランチ" value={<span style={{ fontFamily: 'IBM Plex Mono, monospace', fontSize: 11 }}>{task.branch}</span>} />
|
|
<Row label="作成日時" value={new Date(task.createdAt).toLocaleString('ja-JP')} />
|
|
<Row label="更新日時" value={relativeTime(task.updatedAt)} />
|
|
<Row label="担当" value={task.assignee} />
|
|
</div>
|
|
|
|
<div style={{ marginTop: 16, display: 'flex', gap: 8 }}>
|
|
<button style={{
|
|
padding: '8px 14px', borderRadius: 10, border: '1px solid #e2e8f0',
|
|
background: '#fff', color: '#475569', fontSize: 12, fontWeight: 600,
|
|
cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap',
|
|
}}>再試行</button>
|
|
<button style={{
|
|
padding: '8px 14px', borderRadius: 10, border: '1px solid #fecaca',
|
|
background: '#fff', color: '#b91c1c', fontSize: 12, fontWeight: 600,
|
|
cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap',
|
|
}}>キャンセル</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ProgressTab({ task }) {
|
|
const events = task.events || [];
|
|
return (
|
|
<div style={{ padding: 16, overflowY: 'auto' }}>
|
|
<div style={{ fontSize: 10, fontFamily: 'IBM Plex Mono, monospace', color: '#94a3b8', letterSpacing: '.08em', marginBottom: 8 }}>ACTIVITY</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 20 }}>
|
|
{events.map((e, i) => (
|
|
<div key={i} style={{ display: 'flex', gap: 10, fontSize: 12 }}>
|
|
<div style={{ flexShrink: 0, marginTop: 3 }}>
|
|
<div style={{ width: 8, height: 8, borderRadius: 9999, background: e.kind === 'error' ? '#dc2626' : e.kind === 'ok' ? '#16a34a' : '#3b82f6' }} />
|
|
</div>
|
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
<div style={{ color: '#0f172a', fontWeight: 600 }}>{e.label}</div>
|
|
<div style={{ color: '#64748b', fontSize: 11 }}>{e.meta}</div>
|
|
</div>
|
|
<div style={{ fontSize: 10, color: '#94a3b8', fontFamily: 'IBM Plex Mono, monospace' }}>{e.time}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div style={{ fontSize: 10, fontFamily: 'IBM Plex Mono, monospace', color: '#94a3b8', letterSpacing: '.08em', marginBottom: 6 }}>ACTIVITY.LOG</div>
|
|
<div style={{
|
|
background: '#0f172a', color: '#e2e8f0',
|
|
fontFamily: 'IBM Plex Mono, monospace', fontSize: 11, lineHeight: 1.6,
|
|
padding: 12, borderRadius: 8, whiteSpace: 'pre', overflowX: 'auto',
|
|
}}>
|
|
{`[10:42:18] ` + String.fromCharCode(9432) + ` starting worker for task #` + task.id + `
|
|
[10:42:19] ` + String.fromCharCode(9432) + ` branch: ` + task.branch + `
|
|
[10:42:21] ` + String.fromCharCode(9432) + ` piece: ` + task.piece + `
|
|
[10:42:22] ` + String.fromCharCode(9655) + ` /brainstorm
|
|
[10:43:04] ` + String.fromCharCode(10003) + ` /plan (12 steps)
|
|
[10:43:05] ` + String.fromCharCode(9655) + ` /implement
|
|
[10:44:58] ` + String.fromCharCode(10003) + ` tests passed`}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function DetailPanel({ task, onClose }) {
|
|
const [tab, setTab] = React.useState('overview');
|
|
return (
|
|
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', background: '#f8fafc', borderLeft: '1px solid #e2e8f0' }}>
|
|
<div style={{
|
|
flexShrink: 0, padding: '12px 16px', borderBottom: '1px solid #e2e8f0', background: '#fff',
|
|
display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
|
|
}}>
|
|
<div style={{ minWidth: 0 }}>
|
|
<div style={{ fontSize: 10, fontFamily: 'IBM Plex Mono, monospace', color: '#94a3b8', letterSpacing: '.08em' }}>DETAIL</div>
|
|
<div style={{ fontSize: 13, fontWeight: 700, color: '#0f172a', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>#{task.id} {task.title}</div>
|
|
</div>
|
|
<button onClick={onClose} style={{
|
|
width: 28, height: 28, borderRadius: 8, border: '1px solid #e2e8f0',
|
|
background: '#fff', color: '#64748b', cursor: 'pointer', display: 'inline-flex',
|
|
alignItems: 'center', justifyContent: 'center',
|
|
}}><IconClose width={12} height={12} /></button>
|
|
</div>
|
|
<Tabs tab={tab} onTab={setTab} />
|
|
<div style={{ flex: 1, minHeight: 0, overflow: 'hidden' }}>
|
|
{tab === 'overview' && <OverviewTab task={task} />}
|
|
{tab === 'progress' && <ProgressTab task={task} />}
|
|
{tab === 'subtasks' && (
|
|
<div style={{ padding: 16, fontSize: 13, color: '#64748b' }}>
|
|
サブタスクはこのタスクにありません。
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
window.DetailPanel = DetailPanel;
|