feat(iwooos): expose security source health

This commit is contained in:
ogt
2026-07-11 20:26:05 +08:00
parent caf208315e
commit 2727d5a9ab
3 changed files with 44 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import {
Bot,
Boxes,
CheckCircle2,
Database,
RefreshCw,
Radar,
ShieldAlert,
@@ -41,6 +42,7 @@ type Copy = {
priority: string;
audit: string;
aiTrace: string;
sources: string;
sameRunMissing: string;
unavailable: string;
refresh: string;
@@ -65,6 +67,7 @@ const COPY: Record<"zh" | "en", Copy> = {
priority: "優先處理",
audit: "稽核軌跡",
aiTrace: "AI 軌跡",
sources: "資料源",
sameRunMissing: "尚無同 run 閉環證據",
unavailable: "Live readback 無法取得",
refresh: "重新整理資安控制平面",
@@ -87,6 +90,7 @@ const COPY: Record<"zh" | "en", Copy> = {
priority: "Priorities",
audit: "Audit trail",
aiTrace: "AI trace",
sources: "Sources",
sameRunMissing: "No same-run closure evidence",
unavailable: "Live readback unavailable",
refresh: "Refresh security control plane",
@@ -388,7 +392,7 @@ export function SecurityAssetControlPlaneCockpit({
<div className="grid gap-3 lg:grid-cols-[1fr_1.35fr]">
<div className="border border-[#d8d3c7] bg-white p-3">
<div className="mb-2 text-[11px] font-semibold text-[#4f4d47]">{copy.audit}</div>
<div className="grid grid-cols-3 gap-2 text-center">
<div className="grid grid-cols-4 gap-2 text-center">
<div>
<div className="font-mono text-sm font-semibold text-[#141413]">
{payload.security_audit.automation_operation_count_24h}
@@ -407,7 +411,30 @@ export function SecurityAssetControlPlaneCockpit({
</div>
<div className="mt-1 text-[9px] text-[#77736a]">{copy.aiTrace}</div>
</div>
<div>
<div className="font-mono text-sm font-semibold text-[#141413]">
{payload.summary.ready_source_count}/{payload.summary.source_count}
</div>
<div className="mt-1 text-[9px] text-[#77736a]">{copy.sources}</div>
</div>
</div>
{payload.summary.unavailable_source_count > 0 ? (
<div className="mt-3 flex flex-wrap gap-1 border-t border-[#ebe8df] pt-2">
{payload.source_health
.filter((source) => source.status !== "ready")
.slice(0, 4)
.map((source) => (
<span
key={source.source_id}
className="inline-flex min-w-0 items-center gap-1 border border-[#e2a29b] bg-[#fff2f0] px-1.5 py-0.5 font-mono text-[9px] text-[#9f2f25]"
title={source.reason_code ?? source.source_id}
>
<Database className="h-3 w-3 shrink-0" aria-hidden="true" />
<span className="max-w-32 truncate">{source.source_id}</span>
</span>
))}
</div>
) : null}
{!payload.ai_automation.same_run_closed_loop_proven ? (
<div className="mt-3 flex items-center gap-1.5 border-t border-[#ebe8df] pt-2 text-[10px] font-medium text-[#9f2f25]">
<TriangleAlert className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />