// TaskList — FilterBar + LocalTaskListItem recreation
const SORT_OPTIONS = [
{ value: 'updated', label: '新しい順' },
{ value: 'status', label: 'ステータス順' },
{ value: 'title', label: 'タイトル順' },
];
function SortMenu({ sort, onSort }) {
const [open, setOpen] = React.useState(false);
const ref = React.useRef(null);
React.useEffect(() => {
const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener('mousedown', h); return () => document.removeEventListener('mousedown', h);
}, []);
const current = SORT_OPTIONS.find(o => o.value === sort) || SORT_OPTIONS[0];
return (
{open && (
{SORT_OPTIONS.map(o => (
))}
)}
);
}
function FilterBar({ status, onStatus, search, onSearch, sort, onSort, counts, total }) {
const columns = ['queued', 'running', 'waiting_human', 'waiting_subtasks', 'retry', 'succeeded', 'failed', 'cancelled'];
const chipStyle = (active) => ({
flexShrink: 0, padding: '6px 10px', borderRadius: 9999,
fontSize: 11, fontWeight: 700, whiteSpace: 'nowrap', cursor: 'pointer',
border: '1px solid ' + (active ? '#2563eb' : '#e2e8f0'),
background: active ? '#eff6ff' : '#fff',
color: active ? '#1d4ed8' : '#64748b',
fontFamily: 'inherit',
});
return (
{columns.map(s => (
))}
);
}
function TaskItem({ task, active, onClick }) {
return (
);
}
function TaskList({ tasks, activeId, onSelect, filters, setFilters, onOpenCreate, loading, error, onRetry }) {
const counts = {};
for (const s of ['queued', 'running', 'waiting_human', 'waiting_subtasks', 'retry', 'succeeded', 'failed', 'cancelled']) {
counts[s] = tasks.filter(t => t.status === s).length;
}
const running = counts.running || 0;
const waiting = (counts.waiting_human || 0) + (counts.waiting_subtasks || 0);
const failed = counts.failed || 0;
const filtered = tasks
.filter(t => filters.status === 'all' || t.status === filters.status)
.filter(t => !filters.search || (t.title + t.body).toLowerCase().includes(filters.search.toLowerCase()))
.sort((a, b) => filters.sort === 'title' ? a.title.localeCompare(b.title) : b.updatedAt - a.updatedAt);
const hasSearch = !!filters.search || filters.status !== 'all';
return (
{tasks.length} 件
·
{running} 実行中
{waiting} 待機
{failed > 0 && {failed} 失敗}
setFilters(f => ({ ...f, status: s }))}
search={filters.search} onSearch={(q) => setFilters(f => ({ ...f, search: q }))}
sort={filters.sort} onSort={(s) => setFilters(f => ({ ...f, sort: s }))}
counts={counts} total={tasks.length}
/>
{loading &&
}
{!loading && error && (
)}
{!loading && !error && filtered.map(t =>
onSelect(t.id)} />)}
{!loading && !error && filtered.length === 0 && (
hasSearch ? (
}
title="該当するタスクはありません"
hint="検索ワードやステータスフィルタを変えてみてください。"
action={
}
/>
) : (
}
title="まだ依頼がありません"
hint="左上の「新しい依頼」から最初のタスクを作成できます。"
/>
)
)}
);
}
window.TaskList = TaskList;