feat(governance): 顯性化 AI Agent 日週月報主看板
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m39s
CD Pipeline / build-and-deploy (push) Failing after 30m25s
CD Pipeline / post-deploy-checks (push) Has been skipped

This commit is contained in:
Your Name
2026-06-16 11:44:21 +08:00
parent bb459d59f9
commit 9f4ed2854e
5 changed files with 246 additions and 1 deletions

View File

@@ -4000,6 +4000,184 @@ export function AutomationInventoryTab() {
]}
/>
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 14, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, minWidth: 0 }}>
<div style={{
width: 38,
height: 38,
borderRadius: 8,
border: '0.5px solid #4f46e540',
background: 'rgba(79,70,229,0.08)',
color: '#4f46e5',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}>
<FileText size={18} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 5, minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 18, fontWeight: 700, color: '#141413', lineHeight: 1.15, overflowWrap: 'anywhere' }}>
{t('reportStatusBoard.heroTitle')}
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, color: '#5c5a55', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
{t('reportStatusBoard.heroSubtitle', {
current: reportStatusBoard.program_status.current_task_id,
percent: reportStatusOverall,
next: reportStatusBoard.program_status.next_task_id,
})}
</span>
</div>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6, minWidth: 0 }}>
<Chip value={t('reportStatusBoard.source', {
generated: formatDateTime(reportStatusBoard.generated_at),
current: reportStatusBoard.program_status.current_task_id,
next: reportStatusBoard.program_status.next_task_id,
})} muted />
<Chip value={t('reportStatusBoard.heroCadenceVisible', {
daily: String(reportStatusBoard.report_completion_truth.daily_report_visible),
weekly: String(reportStatusBoard.report_completion_truth.weekly_report_visible),
monthly: String(reportStatusBoard.report_completion_truth.monthly_report_visible),
})} />
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#f8faff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, color: '#4f5565', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
{t('reportStatusBoard.heroStatusLine')}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('reportStatusBoard.flags.send', { value: String(reportStatusBoard.activation_boundaries.telegram_send_enabled) })} muted />
<Chip value={t('reportStatusBoard.flags.queue', { value: String(reportStatusBoard.activation_boundaries.gateway_queue_write_enabled) })} muted />
<Chip value={t('reportStatusBoard.flags.analysis', { value: String(reportStatusBoard.activation_boundaries.ai_analysis_run_enabled) })} muted />
<Chip value={t('reportStatusBoard.flags.optimization', { value: String(reportStatusBoard.activation_boundaries.production_optimization_write_enabled) })} muted />
<Chip value={t('reportStatusBoard.flags.highApproval', { value: String(reportStatusBoard.activation_boundaries.high_risk_requires_human_approval) })} />
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(148px, 1fr))', gap: 10 }} className="automation-inventory-live-read-kpi-grid">
<div style={{ padding: 11, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroMetrics.visibleReports')}</SmallLabel>
<div style={{ marginTop: 6, fontFamily: 'Syne, sans-serif', fontSize: 25, fontWeight: 700, color: '#22C55E', lineHeight: 1 }}>
{reportStatusBoard.rollups.completed_report_count}/{reportStatusCards}
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroMetrics.workload')}</SmallLabel>
<div style={{ marginTop: 6, fontFamily: 'Syne, sans-serif', fontSize: 25, fontWeight: 700, color: '#141413', lineHeight: 1 }}>
{reportStatusDone}/{reportStatusWorkload}
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroMetrics.waitingApproval')}</SmallLabel>
<div style={{ marginTop: 6, fontFamily: 'Syne, sans-serif', fontSize: 25, fontWeight: 700, color: reportStatusWaitingApproval > 0 ? '#F59E0B' : '#22C55E', lineHeight: 1 }}>
{reportStatusWaitingApproval}
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroMetrics.liveTelegram')}</SmallLabel>
<div style={{ marginTop: 6, fontFamily: 'Syne, sans-serif', fontSize: 25, fontWeight: 700, color: reportStatusBoard.rollups.live_telegram_send_count === 0 ? '#22C55E' : '#EF4444', lineHeight: 1 }}>
{reportStatusBoard.rollups.live_telegram_send_count}
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroMetrics.liveOptimization')}</SmallLabel>
<div style={{ marginTop: 6, fontFamily: 'Syne, sans-serif', fontSize: 25, fontWeight: 700, color: reportStatusLiveOptimization === 0 ? '#22C55E' : '#EF4444', lineHeight: 1 }}>
{reportStatusLiveOptimization}
</div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{reportStatusBoard.report_status_cards.map(card => (
<div key={`hero-${card.cadence_id}`} style={{ padding: 12, border: '0.5px solid #c7d2fe', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 9, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 15, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
{card.display_name}
</span>
<Chip value={redisDryRunValueLabel('agents', card.owner_agent)} muted />
</div>
<MiniBar
label={t('reportStatusBoard.heroLabels.reportProgress')}
value={card.completion_percent}
detail={t('reportStatusBoard.heroLabels.reportDetail', {
sections: card.sections_count,
charts: card.chart_count,
work: card.work_units_total,
live: card.live_delivery_count,
})}
tone="ok"
/>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={card.contract_state} muted />
<Chip value={card.delivery_state} muted />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#62636b', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{t('reportStatusBoard.heroLabels.nextGate', { gate: card.next_gate })}
</span>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.1fr) minmax(0, 0.9fr)', gap: 10 }} className="automation-inventory-live-read-grid">
<div style={{ padding: 12, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 9, minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroAgentTitle')}</SmallLabel>
{reportStatusBoard.agent_status_reports.map(agent => {
const progress = agent.work_units_total > 0 ? Math.round((agent.work_units_done / agent.work_units_total) * 100) : 0
return (
<div key={`hero-agent-${agent.agent_id}`} style={{ display: 'grid', gridTemplateColumns: '104px minmax(0, 1fr)', gap: 9, alignItems: 'center', minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
{agent.display_name}
</span>
<MiniBar
label={t('reportStatusBoard.heroLabels.agentWorkload')}
value={progress}
detail={t('reportStatusBoard.heroLabels.agentDetail', {
done: agent.work_units_done,
total: agent.work_units_total,
approval: agent.work_units_waiting_approval,
live: agent.live_runtime_work_units_24h,
})}
tone={progress >= 85 ? 'ok' : 'warn'}
/>
</div>
)
})}
</div>
<div style={{ padding: 12, border: '0.5px solid #dde3ff', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 9, minWidth: 0 }}>
<SmallLabel>{t('reportStatusBoard.heroChartTitle')}</SmallLabel>
{visibleReportStatusCharts.map(chart => {
const maxValue = Math.max(...chart.series.map(item => item.value), 1)
return (
<div key={`hero-chart-${chart.chart_id}`} style={{ display: 'flex', flexDirection: 'column', gap: 6, minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
{chart.display_name}
</span>
{chart.series.map(item => (
<div key={`hero-chart-${chart.chart_id}-${item.label}`} style={{ display: 'grid', gridTemplateColumns: '76px minmax(0, 1fr) 36px', gap: 8, alignItems: 'center', minWidth: 0 }}>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#605f59', overflowWrap: 'anywhere' }}>
{item.label}
</span>
<div style={{ height: 7, borderRadius: 999, background: '#e7e3d8', overflow: 'hidden' }}>
<div style={{ height: '100%', width: `${Math.max(4, Math.round((item.value / maxValue) * 100))}%`, borderRadius: 999, background: toneColor(item.tone) }} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, fontWeight: 700, color: toneColor(item.tone), textAlign: 'right' }}>
{item.value}
</span>
</div>
))}
</div>
)
})}
</div>
</div>
</div>
</GlassCard>
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 13, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>