Files
sanguo_moziplus_v2/src/frontend/src/pages/Briefing.tsx
T
2026-05-17 06:16:22 +08:00

111 lines
3.3 KiB
TypeScript

// AI Briefing 页面
import React, { useState, useEffect, useCallback } from 'react';
import * as api from '../api';
import type { Task, Project } from '../types';
export function Briefing() {
const [projects, setProjects] = useState<Project[]>([]);
const [selectedProject, setSelectedProject] = useState<string>('');
const [tasks, setTasks] = useState<Task[]>([]);
const [briefing, setBriefing] = useState<string>('');
const loadProjects = useCallback(async () => {
try {
const data = await api.listProjects();
setProjects(data);
if (data.length > 0 && !selectedProject) {
setSelectedProject(data[0].id);
}
} catch { /* ignore */ }
}, [selectedProject]);
useEffect(() => { loadProjects(); }, [loadProjects]);
const generateBriefing = useCallback(async () => {
if (!selectedProject) return;
try {
const data = await api.listTasks(selectedProject);
setTasks(data);
// 本地生成简要汇报
const done = data.filter(t => t.status === 'done');
const working = data.filter(t => t.status === 'working');
const failed = data.filter(t => t.status === 'failed');
const pending = data.filter(t => t.status === 'pending');
const lines = [
`📊 项目日报`,
``,
`总任务数: ${data.length}`,
`✅ 已完成: ${done.length}`,
`🔄 进行中: ${working.length}`,
`⏳ 待处理: ${pending.length}`,
`❌ 失败: ${failed.length}`,
``,
];
if (working.length > 0) {
lines.push(`## 进行中的任务`);
working.forEach(t => lines.push(`- [${t.id.substring(0, 8)}] ${t.title} (${t.assignee || '未分配'})`));
lines.push('');
}
if (failed.length > 0) {
lines.push(`## 失败的任务`);
failed.forEach(t => lines.push(`- [${t.id.substring(0, 8)}] ${t.title}`));
lines.push('');
}
if (done.length > 0) {
lines.push(`## 最近完成`);
done.slice(-5).forEach(t => lines.push(`- [${t.id.substring(0, 8)}] ${t.title}`));
}
setBriefing(lines.join('\n'));
} catch { /* ignore */ }
}, [selectedProject]);
return (
<div>
<h2 className="page-title">AI Briefing</h2>
<div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
<select
value={selectedProject}
onChange={e => setSelectedProject(e.target.value)}
style={{
padding: '6px 10px',
borderRadius: 'var(--radius)',
border: '1px solid var(--line)',
background: 'var(--bg)',
color: 'var(--fg)',
}}
>
<option value=""></option>
{projects.map(p => (
<option key={p.id} value={p.id}>{p.name}</option>
))}
</select>
<button className="btn btn-primary" onClick={generateBriefing}>
</button>
</div>
{briefing && (
<div className="card">
<pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'var(--font)', lineHeight: 1.6 }}>
{briefing}
</pre>
</div>
)}
{!briefing && (
<div className="card">
<p style={{ color: 'var(--muted)' }}>"生成汇报"</p>
</div>
)}
</div>
);
}