assistant/apps/admin/src/pages/ProgressPage.tsx

33 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Adapted from the original A2A ProgressPage's ledger summary, table and detail rail.
// Counts describe persisted local tasks and records, with no inferred discovery/coverage states.
import { useMemo, useState } from 'react';
import { Button, Select } from 'antd';
import { RefreshCw } from 'lucide-react';
import { EmptyState, ResourceState } from '../components/States';
import { platformLabels, StatusTag, formatTime, useResource } from '../components/common';
import type { LearningTask, Overview, PageResult } from '../types';
export function ProgressPage({ navigate }: { navigate: (path: string) => void }) {
const resource = useResource<Overview>('/learning/overview');
const taskResource = useResource<PageResult<LearningTask>>('/learning/tasks');
const [selected, setSelected] = useState('all');
const campaigns = taskResource.data?.items || [];
const platforms = useMemo(() => [...new Set(campaigns.map(item => item.platform))], [campaigns]);
const filtered = selected === 'all' ? campaigns : campaigns.filter(item => item.platform === selected);
const data = resource.data;
const refresh = () => { void resource.reload(); void taskResource.reload(); };
return <div className="page-list">
<div className="page-heading"><div className="learning-heading-context"><div className="learning-heading-text"><h1>账本总览</h1><p className="subtitle">{data ? `${data.tasks} 个批次 · ${data.candidates} 个候选 · ${data.validations} 条验证` : '查看持久化的学习与验证记录'}</p></div></div><div className="heading-action"><Button className="quiet-button" icon={<RefreshCw size={16} />} loading={resource.loading || taskResource.loading} onClick={refresh}>刷新账本</Button></div></div>
<ResourceState loading={resource.loading || taskResource.loading} error={resource.error || taskResource.error || null} onRetry={refresh} />
{data && taskResource.data && !resource.error && !taskResource.error && <>
<section className="section ledger-summary"><div className="section-heading"><h2>各平台学习批次</h2><span className="meta">按实际任务状态统计</span></div><div className="table-wrap table-scroll" tabIndex={0} aria-label="平台学习总览"><table className="plain-table"><thead><tr><th>来源平台</th><th>批次总数</th><th>待开始</th><th>观察中</th><th>等待人工登录</th><th>会话关闭 / 暂停</th></tr></thead><tbody>{platforms.map(platform => {
const rows = campaigns.filter(item => item.platform === platform);
return <tr key={platform}><td>{platformLabels[platform]}</td><td>{rows.length}</td><td>{rows.filter(item => ['draft', 'created'].includes(item.status)).length}</td><td>{rows.filter(item => ['observing', 'ready'].includes(item.status)).length}</td><td>{rows.filter(item => ['awaiting_user', 'waiting_login'].includes(item.status)).length}</td><td>{rows.filter(item => ['closed', 'paused'].includes(item.status)).length}</td></tr>;
})}</tbody></table></div></section>
<div className="content-split content-fill"><section className="table-fill"><div className="section-heading"><h2>学习入口</h2><Select aria-label="筛选来源平台" value={selected} onChange={setSelected} className="quiet-control" style={{ minWidth: 176 }} options={[{ value: 'all', label: '全部平台' }, ...platforms.map(value => ({ value, label: platformLabels[value] }))]} /></div>
{filtered.length ? <div className="table-wrap table-scroll" tabIndex={0} aria-label="学习入口列表"><table className="plain-table compact"><thead><tr><th>来源</th><th>学习目标</th><th>状态</th><th>记录时间</th></tr></thead><tbody>{filtered.map(item => <tr key={item.id}><td>{platformLabels[item.platform]}</td><td><a href={`#/tasks/${item.id}`}>{item.name}</a><p className="muted small">{item.goal}</p></td><td><StatusTag status={item.status} /></td><td>{formatTime(item.created_at)}</td></tr>)}</tbody></table></div> : <EmptyState title="暂无学习入口" description="从学习批次页创建目标后,记录将保存在这里。" action={<Button data-testid="view-task-list" onClick={() => navigate('/tasks')}>查看学习批次</Button>} />}
</section><aside className="detail-rail"><h2>验证留档</h2><dl className="metadata"><dt>候选固定版本</dt><dd>{data.candidates}</dd><dt>验证通过记录</dt><dd>{data.passed}</dd><dt>验证失败记录</dt><dd>{data.failed}</dd><dt>已发布版本</dt><dd>{data.published}</dd></dl><p className="muted small">Windows 与 macOS 分别保留实际验证证据,平台模板需逐步观察与验收。</p><Button className="quiet-button" onClick={() => navigate('/validations')}>查看脚本测试</Button></aside></div>
</>}
</div>;
}