feat(governance): 顯性化 AI Agent 日週月報主看板
This commit is contained in:
@@ -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' }}>
|
||||
|
||||
Reference in New Issue
Block a user