33 lines
4.6 KiB
TypeScript
33 lines
4.6 KiB
TypeScript
// 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>;
|
||
}
|