feat(web): show owner response review outcomes

This commit is contained in:
Your Name
2026-05-21 16:28:03 +08:00
parent 98f74ecee3
commit 13ca0bd0df
8 changed files with 430 additions and 3 deletions

View File

@@ -147,6 +147,14 @@ type OwnerResponseIntakeSafetyRule = {
tone: 'steady' | 'warn' | 'locked'
}
type OwnerResponseReviewOutcomeLane = {
key: string
lane: string
value: string
icon: typeof ShieldCheck
tone: 'steady' | 'warn' | 'locked'
}
type CoverageGroup = {
key: string
count: string
@@ -620,6 +628,35 @@ const ownerResponseIntakeSafetyBoundaries = [
'kali_execute_authorized=false',
]
const ownerResponseReviewOutcomeLanes: OwnerResponseReviewOutcomeLane[] = [
{ key: 'remainWaiting', lane: 'O1', value: '0', icon: Clock3, tone: 'warn' },
{ key: 'needsEvidence', lane: 'O2', value: '0', icon: FileWarning, tone: 'warn' },
{ key: 'readyForHumanReview', lane: 'O3', value: '0', icon: ClipboardCheck, tone: 'warn' },
{ key: 'quarantined', lane: 'O4', value: '0', icon: Lock, tone: 'locked' },
{ key: 'rejected', lane: 'O5', value: '0', icon: AlertTriangle, tone: 'locked' },
{ key: 'readonlyUpdate', lane: 'O6', value: '0', icon: FileText, tone: 'steady' },
{ key: 'humanDecisionRequired', lane: 'O7', value: '0', icon: ListChecks, tone: 'locked' },
]
const ownerResponseReviewOutcomeBoundaries = [
'owner_response_review_outcome_lane_count=7',
'owner_response_review_ready_count=0',
'owner_response_review_accepted_count=0',
'owner_response_review_escalated_count=0',
'owner_response_review_runtime_gate_count=0',
'owner_response_review_auto_decision_allowed=false',
'owner_response_review_human_decision_required=true',
'owner_response_review_only_updates_readonly_state=true',
'runtime_execution_authorized=false',
'active_runtime_gate_count=0',
'action_buttons_allowed=false',
'not_authorization=true',
'repo_creation_authorized=false',
'refs_sync_authorized=false',
'workflow_modification_authorized=false',
'github_primary_switch_authorized=false',
]
const coverageGroups: CoverageGroup[] = [
{
key: 'signals',
@@ -1965,6 +2002,126 @@ function OwnerResponseIntakeSafetyBoard() {
)
}
function OwnerResponseReviewOutcomeLaneCard({ item }: { item: OwnerResponseReviewOutcomeLane }) {
const t = useTranslations('iwooos.ownerResponseReviewOutcomeBoard')
const Icon = item.icon
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
return (
<div style={{ ...band, minHeight: 190, padding: 16, ...textWrap }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
<Icon size={18} color={toneColors[item.tone]} style={{ flex: '0 0 auto' }} />
<span style={{ fontSize: 11, color: '#87867f' }}>{t('laneLabel')}</span>
</div>
<span style={{ fontSize: 11, color: toneColors[item.tone] }}>{item.lane}</span>
</div>
<div style={{ fontSize: 28, fontWeight: 700, color: toneColors[item.tone], marginTop: 12, lineHeight: 1 }}>
{item.value}
</div>
<h2 style={{ fontSize: 14, margin: '10px 0 6px', color: '#141413' }}>
{t(`items.${item.key}.title` as never)}
</h2>
<p style={{ fontSize: 12, lineHeight: 1.55, color: '#6f6d66', margin: 0, ...textWrap }}>
{t(`items.${item.key}.body` as never)}
</p>
<div style={{ marginTop: 10, display: 'grid', gap: 5 }}>
<span style={{ fontSize: 11, color: '#87867f' }}>{t('resultLabel')}</span>
<span style={{ fontSize: 11, color: toneColors[item.tone], lineHeight: 1.45, ...textWrap }}>
{t(`items.${item.key}.result` as never)}
</span>
<span style={{ fontSize: 11, color: '#87867f', marginTop: 4 }}>{t('guardLabel')}</span>
<span style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45, ...textWrap }}>
{t(`items.${item.key}.guard` as never)}
</span>
</div>
</div>
)
}
function OwnerResponseReviewOutcomeBoard() {
const t = useTranslations('iwooos.ownerResponseReviewOutcomeBoard')
const summaryItems = [
{ key: 'lanes', value: '7', tone: 'warn' as const },
{ key: 'ready', value: '0', tone: 'warn' as const },
{ key: 'accepted', value: '0', tone: 'locked' as const },
{ key: 'runtime', value: '0', tone: 'locked' as const },
]
return (
<section style={{ marginBottom: 14 }} data-testid="iwooos-owner-response-review-outcome-board">
<div style={{ marginBottom: 14 }}>
<h2 style={{ fontSize: 16, margin: 0 }}>{t('title')}</h2>
<p style={{ fontSize: 12, color: '#6f6d66', margin: '6px 0 0', lineHeight: 1.55 }}>
{t('subtitle')}
</p>
</div>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
gap: 10,
marginBottom: 12,
}}
>
{summaryItems.map(item => (
<div key={item.key} style={{ ...band, padding: 14, minHeight: 96 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ fontSize: 11, color: '#87867f' }}>{t(`summary.${item.key}.label` as never)}</span>
<ToneDot tone={item.tone} />
</div>
<div style={{ fontSize: 24, fontWeight: 700, lineHeight: 1, marginTop: 9, color: '#141413' }}>
{item.value}
</div>
<p style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45, margin: '8px 0 0' }}>
{t(`summary.${item.key}.detail` as never)}
</p>
</div>
))}
</div>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))',
gap: 12,
}}
>
{ownerResponseReviewOutcomeLanes.map(item => (
<OwnerResponseReviewOutcomeLaneCard key={item.key} item={item} />
))}
</div>
<div style={{ ...band, marginTop: 12, padding: 16 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
<ShieldCheck size={16} color={toneColors.locked} />
<h3 style={{ fontSize: 14, margin: 0 }}>{t('boundaryTitle')}</h3>
</div>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))',
gap: 7,
}}
>
{ownerResponseReviewOutcomeBoundaries.map(item => (
<span
key={item}
style={{
border: '0.5px solid #eee9dd',
borderRadius: 8,
padding: '7px 9px',
color: '#4f4c45',
fontSize: 11,
lineHeight: 1.4,
overflowWrap: 'anywhere',
}}
>
{item}
</span>
))}
</div>
</div>
</section>
)
}
function CoverageCard({ item }: { item: CoverageGroup }) {
const t = useTranslations('iwooos.coverage')
const Icon = item.icon
@@ -3102,6 +3259,8 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
<OwnerResponseIntakeSafetyBoard />
<OwnerResponseReviewOutcomeBoard />
<section style={{ marginBottom: 14 }}>
<div style={{ marginBottom: 14 }}>
<h2 style={{ fontSize: 16, margin: 0 }}>{t('awooopReadOnlyLandingReadiness.title')}</h2>