feat(web): add homepage operations map
This commit is contained in:
@@ -318,6 +318,32 @@
|
||||
"km": "KM 健康"
|
||||
}
|
||||
},
|
||||
"homeProductMap": {
|
||||
"eyebrow": "AWOOOI Operations Map",
|
||||
"title": "AI Automation Management Console",
|
||||
"subtitle": "A unified operations map for intake, AI decisions, evidence, approvals, execution, verification, and KM learning, built to show ownership, blockers, and evidence sources at a glance.",
|
||||
"currentGate": "Current claim state",
|
||||
"flowTitle": "Incident lifecycle",
|
||||
"moduleTitle": "Product modules and data sources",
|
||||
"modules": {
|
||||
"awooop": {
|
||||
"module": "AwoooP Run Timeline",
|
||||
"owns": "Intake, deduplication, workflow stages, Telegram callbacks, and historical evidence."
|
||||
},
|
||||
"routing": {
|
||||
"module": "AI Router / Agent Roles",
|
||||
"owns": "GCP-A, GCP-B, host 111, Gemini fallback, and Hermes/OpenClaw/ElephantAlpha role routing."
|
||||
},
|
||||
"execution": {
|
||||
"module": "PlayBook / MCP / Ansible",
|
||||
"owns": "Tool investigation, dry-runs, check-mode, apply gates, and repair evidence."
|
||||
},
|
||||
"learning": {
|
||||
"module": "KM / Governance",
|
||||
"owns": "Stale KM, owner review, SLOs, postmortem learning, and rule updates."
|
||||
}
|
||||
}
|
||||
},
|
||||
"automationDelivery": {
|
||||
"eyebrow": "AI 自動化管理產品面",
|
||||
"title": "目前完成項與待推進項",
|
||||
|
||||
@@ -318,6 +318,32 @@
|
||||
"km": "KM 健康"
|
||||
}
|
||||
},
|
||||
"homeProductMap": {
|
||||
"eyebrow": "AWOOOI Operations Map",
|
||||
"title": "AI 自動化管理介面",
|
||||
"subtitle": "統一呈現事件從接收、判斷、證據、審批、執行到驗證與學習的狀態,讓營運團隊快速掌握責任、阻塞與證據來源。",
|
||||
"currentGate": "目前可宣稱狀態",
|
||||
"flowTitle": "事件生命週期",
|
||||
"moduleTitle": "產品模組與資料來源",
|
||||
"modules": {
|
||||
"awooop": {
|
||||
"module": "AwoooP Run Timeline",
|
||||
"owns": "收件、去重、處理階段、Telegram callback、歷史證據"
|
||||
},
|
||||
"routing": {
|
||||
"module": "AI Router / Agent 分工",
|
||||
"owns": "GCP-A、GCP-B、111、Gemini fallback 與 Hermes/OpenClaw/ElephantAlpha 分工"
|
||||
},
|
||||
"execution": {
|
||||
"module": "PlayBook / MCP / Ansible",
|
||||
"owns": "工具調查、dry-run、check-mode、apply gate 與修復證據"
|
||||
},
|
||||
"learning": {
|
||||
"module": "KM / Governance",
|
||||
"owns": "stale KM、owner review、SLO、事後學習與規則更新"
|
||||
}
|
||||
}
|
||||
},
|
||||
"automationDelivery": {
|
||||
"eyebrow": "AI 自動化管理產品面",
|
||||
"title": "目前完成項與待推進項",
|
||||
|
||||
@@ -1879,6 +1879,60 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
}),
|
||||
},
|
||||
]
|
||||
const productModuleRows: Array<{
|
||||
key: string
|
||||
module: string
|
||||
owns: string
|
||||
evidence: string
|
||||
href: string
|
||||
tone: HomepageWorkTone
|
||||
}> = [
|
||||
{
|
||||
key: 'awooop',
|
||||
module: tDashboard('homeProductMap.modules.awooop.module'),
|
||||
owns: tDashboard('homeProductMap.modules.awooop.owns'),
|
||||
evidence: tDashboard('automationDiagrams.workspace.liveEvidence.intake.metric', {
|
||||
runs: formatAutomationNumber(automationBrief.runsList?.total, runsListLoaded),
|
||||
linked: formatAutomationNumber(recurrenceSummary?.linked_run_total, eventRecurrenceLoaded),
|
||||
}),
|
||||
href: `/${locale}/awooop/runs?project_id=awoooi`,
|
||||
tone: 'live',
|
||||
},
|
||||
{
|
||||
key: 'routing',
|
||||
module: tDashboard('homeProductMap.modules.routing.module'),
|
||||
owns: tDashboard('homeProductMap.modules.routing.owns'),
|
||||
evidence: tDashboard('automationDiagrams.workspace.values.aiRoute', {
|
||||
lane: aiRouteLaneMode,
|
||||
provider: aiRouteSelectedProvider,
|
||||
}),
|
||||
href: `/${locale}/awooop/work-items?project_id=awoooi`,
|
||||
tone: hasAiRouteStatus ? 'live' : 'watching',
|
||||
},
|
||||
{
|
||||
key: 'execution',
|
||||
module: tDashboard('homeProductMap.modules.execution.module'),
|
||||
owns: tDashboard('homeProductMap.modules.execution.owns'),
|
||||
evidence: tDashboard('automationDiagrams.workspace.values.ansible', {
|
||||
checkMode: formatAutomationNumber(executionBackend?.ansible_check_mode_total, automationQualityLoaded),
|
||||
pending: formatAutomationNumber(executionBackend?.ansible_pending_check_mode_total, automationQualityLoaded),
|
||||
blocker: ansibleRuntimeBlockerLabel,
|
||||
}),
|
||||
href: `/${locale}/automation`,
|
||||
tone: ansibleRuntime?.can_run_check_mode ? 'live' : automationQualityAvailable ? 'blocked' : 'watching',
|
||||
},
|
||||
{
|
||||
key: 'learning',
|
||||
module: tDashboard('homeProductMap.modules.learning.module'),
|
||||
owns: tDashboard('homeProductMap.modules.learning.owns'),
|
||||
evidence: tDashboard('automationDiagrams.workspace.liveEvidence.verify.metric', {
|
||||
stale: formatAutomationNumber(kmStaleDisplayCount, kmGovernanceLoaded),
|
||||
ratio: staleRatioLabel,
|
||||
}),
|
||||
href: `/${locale}/knowledge-base`,
|
||||
tone: typeof kmStaleDisplayCount === 'number' && kmStaleDisplayCount > 0 ? 'progress' : hasKmStaleCandidates ? 'live' : 'watching',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<AppLayout locale={locale} showBackground={false} fullBleed>
|
||||
@@ -1894,14 +1948,173 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'calc(100vh - 68px)',
|
||||
minHeight: 'calc(100vh - 68px)',
|
||||
background: '#f5f4ed',
|
||||
fontFamily: 'var(--font-body), monospace',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
overscrollBehavior: 'contain',
|
||||
overflowY: 'visible',
|
||||
}}>
|
||||
|
||||
<section
|
||||
data-testid="homepage-product-map"
|
||||
style={{
|
||||
margin: primarySectionMargin,
|
||||
background: '#fff',
|
||||
borderTop: '0.5px solid #d8d3c7',
|
||||
borderBottom: '0.5px solid #d8d3c7',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: compactViewport ? '1fr' : 'minmax(0, 1.4fr) minmax(260px, 0.6fr)',
|
||||
gap: compactViewport ? 10 : 18,
|
||||
padding: compactViewport ? '14px 12px' : '18px 20px',
|
||||
borderBottom: '0.5px solid #e0ddd4',
|
||||
background: '#fffdf8',
|
||||
}}>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 850, color: '#1f5b9b', textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
||||
{tDashboard('homeProductMap.eyebrow')}
|
||||
</div>
|
||||
<h1 style={{ margin: '5px 0 0', fontSize: compactViewport ? 22 : 28, lineHeight: 1.1, fontWeight: 900, color: '#141413' }}>
|
||||
{tDashboard('homeProductMap.title')}
|
||||
</h1>
|
||||
<p style={{ margin: '8px 0 0', maxWidth: 820, fontSize: 13, lineHeight: 1.65, color: '#4e4a43' }}>
|
||||
{tDashboard('homeProductMap.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{
|
||||
alignSelf: 'stretch',
|
||||
display: 'grid',
|
||||
gap: 8,
|
||||
border: `0.5px solid ${automationDeliveryClaimTone.border}`,
|
||||
background: automationDeliveryClaimTone.bg,
|
||||
padding: '11px 12px',
|
||||
}}>
|
||||
<div style={{ fontSize: 10, fontWeight: 850, color: automationDeliveryClaimTone.color, textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
||||
{tDashboard('homeProductMap.currentGate')}
|
||||
</div>
|
||||
<div style={{ fontSize: 16, fontWeight: 900, color: '#141413', lineHeight: 1.2 }}>
|
||||
{automationDeliveryHeadline}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, lineHeight: 1.5, color: '#5f5b52' }}>
|
||||
{automationDeliveryClaimDetail}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: compactViewport ? '1fr' : 'minmax(0, 1.35fr) minmax(320px, 0.85fr)',
|
||||
gap: compactViewport ? 10 : 14,
|
||||
padding: compactViewport ? 12 : 16,
|
||||
}}>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, marginBottom: 10 }}>
|
||||
<h2 style={{ margin: 0, fontSize: 15, fontWeight: 900, color: '#141413' }}>
|
||||
{tDashboard('homeProductMap.flowTitle')}
|
||||
</h2>
|
||||
<a href={`/${locale}/awooop/runs?project_id=awoooi`} style={{ fontSize: 11, fontWeight: 800, color: '#1f5b9b', textDecoration: 'none' }}>
|
||||
{tDashboard('automationDelivery.openRuns')}
|
||||
</a>
|
||||
</div>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: compactViewport ? '1fr' : 'repeat(4, minmax(0, 1fr))',
|
||||
gap: 8,
|
||||
}}>
|
||||
{automationFlowStages.map((stage, index) => {
|
||||
const tone = automationWorkToneStyle[stage.tone]
|
||||
return (
|
||||
<a
|
||||
key={`product-map-${stage.key}`}
|
||||
href={`/${locale}?blueprint_stage=${stage.key}#homepage-ai-command-map`}
|
||||
onClick={() => setSelectedBlueprintStageKey(stage.key as HomepageBlueprintStageKey)}
|
||||
style={{
|
||||
minWidth: 0,
|
||||
border: `0.5px solid ${tone.border}`,
|
||||
background: tone.bg,
|
||||
padding: '9px 10px',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
||||
<span style={{ fontSize: 10, fontWeight: 900, color: tone.color, fontFamily: "'JetBrains Mono', monospace" }}>
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<span style={{ fontSize: 9, fontWeight: 850, color: tone.color, textTransform: 'uppercase', whiteSpace: 'nowrap' }}>
|
||||
{stage.status}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ marginTop: 8, fontSize: 12, fontWeight: 900, color: '#141413', lineHeight: 1.25 }}>
|
||||
{stage.title}
|
||||
</div>
|
||||
<div style={{ marginTop: 6, fontSize: 10, lineHeight: 1.45, color: '#5f5b52', minHeight: 30, overflow: 'hidden' }}>
|
||||
{stage.liveEvidence.metric}
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, marginBottom: 10 }}>
|
||||
<h2 style={{ margin: 0, fontSize: 15, fontWeight: 900, color: '#141413' }}>
|
||||
{tDashboard('homeProductMap.moduleTitle')}
|
||||
</h2>
|
||||
<a href={`/${locale}/topology`} style={{ fontSize: 11, fontWeight: 800, color: '#1f5b9b', textDecoration: 'none' }}>
|
||||
{tDashboard('automationDiagrams.openTopology')}
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gap: 1, border: '0.5px solid #e0ddd4', background: '#e0ddd4' }}>
|
||||
{productModuleRows.map((row) => {
|
||||
const tone = automationWorkToneStyle[row.tone]
|
||||
return (
|
||||
<a
|
||||
key={row.key}
|
||||
href={row.href}
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: compactViewport ? '1fr' : '0.72fr 1fr',
|
||||
gap: 8,
|
||||
padding: '9px 10px',
|
||||
background: '#fff',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontSize: 12, fontWeight: 900, color: '#141413' }}>{row.module}</div>
|
||||
<div style={{ marginTop: 3, fontSize: 10, lineHeight: 1.4, color: '#5f5b52' }}>{row.owns}</div>
|
||||
</div>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{
|
||||
display: 'inline-flex',
|
||||
maxWidth: '100%',
|
||||
border: `0.5px solid ${tone.border}`,
|
||||
background: tone.bg,
|
||||
color: tone.color,
|
||||
padding: '2px 7px',
|
||||
fontSize: 10,
|
||||
fontWeight: 850,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}>
|
||||
{row.evidence}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{
|
||||
margin: primarySectionMargin,
|
||||
background: '#fff',
|
||||
|
||||
@@ -27696,3 +27696,61 @@ production browser smoke:
|
||||
- Ansible / PlayBook runtime:約 95%;候選 PlayBook 已呈現在 AwoooP,但本 incident 尚無 check/apply 紀錄。
|
||||
- 完整自動修復 production claim:約 3.5%;Gate 5 projection 是可見性與安全閘,不是自動修復成功。真正提升要完成 executor handoff 並用 24h verified_success 拉高。
|
||||
- 完整 AI Agent 自動化飛輪:約 63%;監控、告警、審批、證據鏈、前端可視化更完整,但執行成功率與學習閉環仍是主缺口。
|
||||
|
||||
## 2026-06-02 — Frontend product UX 12-agent audit + homepage operations map
|
||||
|
||||
**背景**:
|
||||
- 使用者指出 production 首頁與 AwoooP/Alerts/Governance 等頁面文字量過大、難以快速判讀流程階段,且首頁無法正常上下滾動。
|
||||
- 本輪以 12 個 read-only agent 分工盤點:IA/navigation、首頁、AwoooP、Alerts、KM/Governance、Observability、Automation/Ansible、Security/IwoooS、i18n、Visual System、Data/API truth、Responsive/A11y。
|
||||
- 主工作區 `/Users/ogt/awoooi` 仍有大量既有 dirty/untracked 變更;本輪在乾淨 worktree `/private/tmp/awoooi-frontend-product-audit-20260602` 基於最新 `gitea/main` 開發,避免混入無關變更。
|
||||
|
||||
**本輪完成**:
|
||||
- 首頁 `/zh-TW` 新增 `homepage-product-map` 首屏區塊:
|
||||
- 產品化呈現 AI 自動化事件生命週期:Alert / Sentry / SigNoz → AwoooP 收件 → OpenClaw / Hermes → MCP 證據 → PlayBook 閘門 → Ansible Check → Approval / Apply → Verify / KM。
|
||||
- 右側以表格化模組列呈現 AwoooP Run Timeline、AI Router / Agent 分工、PlayBook / MCP / Ansible、KM / Governance 的 owner 與資料來源。
|
||||
- 不新增假數字,沿用 production truth-chain / homepage brief / quality summary 的既有資料;資料未回應時顯示暫不可用,不宣稱自動修復完成。
|
||||
- 修正首頁 scroll owner:
|
||||
- 原本 overview 外層 `height: calc(100vh - 68px)` + `overflowY:auto` 造成 document 不是主要 scroll owner,production 實測 document height 只有約 1299px、內層 scrollHeight 約 6023px。
|
||||
- 改為 `minHeight: calc(100vh - 68px)` + `overflowY:visible`,恢復正常 document scroll。
|
||||
- 補 `zh-TW` / `en` i18n:`dashboard.homeProductMap.*`,避免把本輪討論文字硬寫在頁面中。
|
||||
|
||||
**12-agent 盤點收斂**:
|
||||
- IA/navigation:46 個 route、108 個 TSX component 粗盤;主導航是 8 主入口 + legacy + bottom,與「五柱導航」註解不一致。需要下一波收斂孤島頁:`/topology`、`/aiops/timeline`、`/reports`、`/alert-operation-logs`、`/users`。
|
||||
- AwoooP:Runs / Work Items / Approvals 資料結構夠完整,但頁面把 incident、recurrence、repair、source correlation、approval、truth-chain 混在同一閱讀面。下一波需要 Run detail flow / evidence table / approval gate drawer。
|
||||
- Alerts:`/incidents` 目前沒有 `alertname/source/fingerprint/hit_count/source_logs` projection,前端無法判讀重複告警。需要 incident correlation projection。
|
||||
- KM/Governance:後端已有 `knowledge_degradation` / `kb_stale` governance event,但前端 events/query/model 與後端 schema 不完全對齊,operator 看到的是告警而不是 workflow。需要 KM Health tab。
|
||||
- Observability:Monitoring/Alerts/APM 仍偏列表文字,缺 severity x hour heatmap、source matrix、routing funnel、host/service heatmap。`MonitoringPanel` 與 `/dashboard` payload contract 也需對齊。
|
||||
- Automation/Ansible:Ansible 不是一級 ActionType;AutoRepairPanel 只露少量 gate 資訊,未呈現 cooldown / service registry / anti-pattern / verifier / rollback / KM links。需要 AutoRepair gate trace + Ansible coverage table。
|
||||
- Visual System:缺共用 Button / Badge / DataTable / PageHeader / EmptyState / MetricCard primitive,導致頁面大量手刻卡片、文字牆與 emoji icon 殘留。
|
||||
- Data/API truth:AIOps mock、首頁/classic static host catalog、CPU/RAM fallback、Code Review 內網 URL、`NEXT_PUBLIC_API_URL ?? ''` catch-to-empty 是 P1 技術債;需 API-backed truth source 與 public/proxy-safe URLs。
|
||||
- Responsive/A11y:首頁 scroll owner 已先修;仍需 mobile drawer shell、FlowPipeline mobile vertical mode、tab semantics、focus-visible、touch target 與 text contrast 修正。
|
||||
|
||||
**驗證**:
|
||||
- `node -e "JSON.parse(...zh-TW.json); JSON.parse(...en.json); console.log('json ok')"` → `json ok`
|
||||
- `pnpm --filter @awoooi/web typecheck` → success
|
||||
- Local dev:
|
||||
- `NEXT_PUBLIC_API_URL=https://awoooi.wooo.work pnpm --filter @awoooi/web dev -- --hostname 127.0.0.1 --port 3112`
|
||||
- Browser DOM:`productMapPresent=true`、`docScrollHeight=5428`、`bodyScrollHeight=5428`、`navVisible=true`
|
||||
- Desktop screenshot layout audit:`horizontalOverflow=0`、`overflowingCount=0`
|
||||
- Mobile 390px layout audit:`productMapPresent=true`、`horizontalOverflow=0`、`overflowingCount=0`
|
||||
- Production build:
|
||||
- `NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PRIVATE_BUILD_WORKER_COUNT=1 pnpm --filter @awoooi/web build` → success
|
||||
- Build size note:`/[locale]` 32.5 kB / First Load JS 277 kB;`/topology` 492 kB / First Load JS 722 kB,後續需另列 performance debt。
|
||||
|
||||
**下一波優先順序**:
|
||||
1. AwoooP Run detail / Evidence table / Approval gate drawer:讓 operator 一眼知道跑到哪一關、誰負責、缺什麼證據。
|
||||
2. Incident correlation projection:`fingerprint/hit_count/source_logs/Sentry/SigNoz/Telegram/AwoooP run` 進 `/incidents` 與 timeline,解決重複告警不可判讀。
|
||||
3. KM Health / Governance workflow:把 `knowledge_degradation` 從告警文字轉成 stale buckets、owner review、dispatch/verify workflow。
|
||||
4. AutoRepair gate trace + Ansible coverage table:把 PlayBook match、dry-run、policy、approval、verify、KM link 與 Ansible check/apply/diff 變成可掃描表格。
|
||||
5. Frontend primitives / i18n debt:建立 Button/Badge/DataTable/PageHeader/EmptyState/MetricCard,先改 AwoooP 與 Alerts;清 AwoooP/Code Review 硬編文案與內網 URL。
|
||||
|
||||
**目前整體進度(本階段完成後)**:
|
||||
- 全站前端 IA / 可讀性盤點:約 100%;12 個 read-only agent 已完成並已收斂為 wave。
|
||||
- 首頁產品化入口:約 78%;已補 Operations Map 與正常 document scroll,仍需清舊 static/fallback 區塊與拆大檔。
|
||||
- AwoooP / HITL 可視化:約 99.2%;本輪未改 executor handoff,仍缺 Run detail evidence table 與 legacy duplicate reconciliation。
|
||||
- Alerts recurrence / source correlation:約 68%;問題已定位,需新增 API projection 後前端才能準確呈現重複與來源。
|
||||
- KM / Governance workflow:約 58%;後端事件已有,前端 workflow/health tab 尚未完成。
|
||||
- Ansible / AutoRepair gate 可視化:約 52%;候選資料與 runbook 已有,尚缺一級 ActionType / coverage table / gate trace。
|
||||
- Frontend design system / i18n:約 46%;盤點完成,但 primitives 與 AwoooP i18n 大掃除尚未實作。
|
||||
- 完整自動修復 production claim:約 3.5%;本輪是 UX/可視化進展,未提高 verified auto-repair execution success。
|
||||
- 完整 AI Agent 自動化飛輪:約 66%;可視化與產品入口改善,但真正自動執行、驗證、學習閉環仍需下一波 API + executor + governance 工作。
|
||||
|
||||
Reference in New Issue
Block a user