auto-sync: 2026-05-17 06:15:35

This commit is contained in:
cfdaily
2026-05-17 06:15:35 +08:00
parent 66fe2bf4ff
commit c0096f9dcb
+74
View File
@@ -0,0 +1,74 @@
// 任务看板页面
import React from 'react';
import { useTasks } from '../hooks/useApi';
import type { Task } from '../types';
interface Props {
projectId: string | null;
onSelectTask: (task: Task) => void;
}
const STATUS_ORDER = ['pending', 'working', 'review', 'blocked', 'done', 'failed'];
export function TaskBoard({ projectId, onSelectTask }: Props) {
const { tasks, loading, error, refresh } = useTasks(projectId);
if (!projectId) {
return <div className="loading"></div>;
}
if (loading) return <div className="loading">...</div>;
if (error) return <div className="error">: {error}</div>;
// 按状态分组
const grouped: Record<string, Task[]> = {};
for (const task of tasks) {
const status = task.status || 'pending';
if (!grouped[status]) grouped[status] = [];
grouped[status].push(task);
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<h2 className="page-title"></h2>
<button className="btn" onClick={refresh}></button>
</div>
{STATUS_ORDER.map(status => {
const statusTasks = grouped[status] || [];
if (statusTasks.length === 0) return null;
return (
<div key={status} style={{ marginBottom: 20 }}>
<h3 style={{ fontSize: 14, color: 'var(--muted)', marginBottom: 8, textTransform: 'uppercase' }}>
{status} ({statusTasks.length})
</h3>
<div className="task-board">
{statusTasks.map(task => (
<div
key={task.id}
className="task-card"
onClick={() => onSelectTask(task)}
>
<div className="title">{task.title}</div>
<div className="meta">
<span className={`badge badge-${status}`}>{status}</span>
{task.assignee && <span>👤 {task.assignee}</span>}
{task.risk_level && <span> {task.risk_level}</span>}
</div>
</div>
))}
</div>
</div>
);
})}
{tasks.length === 0 && (
<div className="card">
<p style={{ color: 'var(--muted)' }}> API CLI </p>
</div>
)}
</div>
);
}