feat(web): add s49 dispatch flow summary

This commit is contained in:
Your Name
2026-05-24 13:33:24 +08:00
parent f51e1cba5e
commit 504dd136c5
8 changed files with 391 additions and 2 deletions

View File

@@ -135,6 +135,14 @@ type S49OwnerResponseRequestDraftItem = {
tone: 'steady' | 'warn' | 'locked'
}
type S49OwnerResponseDispatchFlowStep = {
key: string
step: string
status: string
icon: typeof ShieldCheck
tone: 'steady' | 'warn' | 'locked'
}
type Pillar = {
key: string
icon: typeof ShieldCheck
@@ -846,6 +854,40 @@ const s49OwnerResponseRequestDraftBoundaries = [
'gitea_disablement_authorized=false',
]
const s49OwnerResponseDispatchFlowSteps: S49OwnerResponseDispatchFlowStep[] = [
{ key: 'workOrder', step: '01', status: '可讀', icon: ClipboardCheck, tone: 'warn' },
{ key: 'envelope', step: '02', status: '空白', icon: FileText, tone: 'warn' },
{ key: 'preflight', step: '03', status: '未通過', icon: ListChecks, tone: 'locked' },
{ key: 'outcome', step: '04', status: '待分流', icon: SearchCheck, tone: 'warn' },
{ key: 'requestDraft', step: '05', status: '草稿', icon: Bell, tone: 'warn' },
{ key: 'manualDispatchGate', step: '06', status: '未開', icon: Lock, tone: 'locked' },
]
const s49OwnerResponseDispatchFlowBoundaries = [
's4_9_owner_response_dispatch_flow_step_count=6',
's4_9_owner_response_dispatch_flow_current_step=request_draft',
's4_9_owner_response_dispatch_flow_completed_count=0',
's4_9_owner_response_dispatch_flow_blocked_count=0',
's4_9_owner_response_request_sent=false',
's4_9_owner_response_request_sent_count=0',
's4_9_owner_response_received_count=0',
's4_9_owner_response_accepted_count=0',
's4_9_owner_response_request_dispatch_authorized=false',
'owner_response_acceptance_gate_open=false',
'audit_events_emitted=0',
'progress_review_authorized=false',
'runtime_execution_authorized=false',
'active_runtime_gate_count=0',
'action_buttons_allowed=false',
'not_authorization=true',
'secret_value_collection_allowed=false',
'repo_creation_authorized=false',
'refs_sync_authorized=false',
'workflow_modification_authorized=false',
'github_primary_switch_authorized=false',
'gitea_disablement_authorized=false',
]
const posturePillars: Pillar[] = [
{ key: 'exposure', icon: Radar, tone: 'warn' },
{ key: 'sourceControl', icon: GitBranch, tone: 'warn' },
@@ -2961,6 +3003,119 @@ function S49OwnerResponseRequestDraftBoard() {
)
}
function S49OwnerResponseDispatchFlowBoard() {
const t = useTranslations('iwooos.s49OwnerResponseDispatchFlow')
const summaryItems = [
{ key: 'steps', value: '6', tone: 'warn' as const },
{ key: 'current', value: '05', tone: 'warn' as const },
{ key: 'sent', value: '0', tone: 'locked' as const },
{ key: 'accepted', value: '0', tone: 'locked' as const },
]
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
return (
<section style={{ marginBottom: 14 }} data-testid="iwooos-s49-owner-response-dispatch-flow-board">
<div style={{ ...band, padding: 16, background: '#fffdf8' }}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 16 }}>
<div>
<div style={{ marginBottom: 14 }}>
<h2 style={{ fontSize: 17, margin: 0 }}>{t('title')}</h2>
<p style={{ fontSize: 12, color: '#6f6d66', margin: '6px 0 0', lineHeight: 1.55, ...textWrap }}>
{t('subtitle')}
</p>
</div>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(132px, 1fr))',
gap: 8,
marginBottom: 14,
}}
>
{summaryItems.map(item => (
<div key={item.key} style={{ border: '0.5px solid #eee9dd', borderRadius: 8, padding: 12 }}>
<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: 23, fontWeight: 700, lineHeight: 1, marginTop: 8, color: '#141413' }}>
{item.value}
</div>
<p style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45, margin: '8px 0 0', ...textWrap }}>
{t(`summary.${item.key}.detail` as never)}
</p>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: 9 }}>
{s49OwnerResponseDispatchFlowSteps.map(item => {
const Icon = item.icon
return (
<div
key={item.key}
style={{
border: '0.5px solid #e7e1d3',
borderRadius: 8,
background: '#fff',
padding: 12,
minHeight: 142,
display: 'grid',
alignContent: 'start',
gap: 7,
...textWrap,
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ fontSize: 11, color: '#87867f' }}>{t('stepLabel')} {item.step}</span>
<Icon size={17} color={toneColors[item.tone]} />
</div>
<div style={{ fontSize: 19, fontWeight: 700, color: toneColors[item.tone], lineHeight: 1.15 }}>
{item.status}
</div>
<h3 style={{ fontSize: 13, margin: 0, color: '#141413', lineHeight: 1.3 }}>
{t(`items.${item.key}.title` as never)}
</h3>
<p style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45, margin: 0, ...textWrap }}>
{t(`items.${item.key}.body` as never)}
</p>
</div>
)
})}
</div>
</div>
<div style={{ ...band, padding: 14, background: '#f7f5ee', alignSelf: 'stretch' }}>
<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>
<p style={{ fontSize: 12, color: '#6f6d66', lineHeight: 1.55, margin: '0 0 10px', ...textWrap }}>
{t('boundaryIntro')}
</p>
<div style={{ display: 'grid', gap: 7 }}>
{s49OwnerResponseDispatchFlowBoundaries.map(item => (
<span
key={item}
style={{
border: '0.5px solid #e0ddd4',
borderRadius: 8,
padding: '7px 9px',
color: '#4f4c45',
fontSize: 11,
lineHeight: 1.4,
background: '#fff',
overflowWrap: 'anywhere',
}}
>
{item}
</span>
))}
</div>
</div>
</div>
</div>
</section>
)
}
function PillarCard({ item }: { item: Pillar }) {
const t = useTranslations('iwooos.pillars')
const Icon = item.icon
@@ -6699,6 +6854,8 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
<HeadlineMovementAcceptanceGateBoard />
<S49OwnerResponseDispatchFlowBoard />
<S49OwnerResponseWorkOrderBoard />
<S49OwnerResponseEnvelopeBoard />