fix(ui): replace work item text wall with visual status
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 46s
CD Pipeline / build-and-deploy (push) Successful in 4m31s
CD Pipeline / post-deploy-checks (push) Has been cancelled
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 46s
CD Pipeline / build-and-deploy (push) Successful in 4m31s
CD Pipeline / post-deploy-checks (push) Has been cancelled
This commit is contained in:
@@ -9035,13 +9035,25 @@
|
|||||||
"blocked": "阻塞"
|
"blocked": "阻塞"
|
||||||
},
|
},
|
||||||
"aiLoopLogSources": {
|
"aiLoopLogSources": {
|
||||||
"eyebrow": "AI Loop source labels",
|
"eyebrow": "P0 visual state",
|
||||||
"title": "LOG grouping and learning route",
|
"title": "Current blocker and repair intake",
|
||||||
"subtitle": "Reads metadata-only tags for the current blocker from the priority work-order so KM, RAG, PlayBook, MCP, Verifier, and AI Agent share one source taxonomy.",
|
"subtitle": "Shows the blocker, SSH diagnosis, and receipt contract first; technical fields are collapsed so the workbench does not read like a long text list.",
|
||||||
"loading": "Loading LOG source labels",
|
"loading": "Loading LOG source labels",
|
||||||
"empty": "No LOG source labels read back yet.",
|
"empty": "No LOG source labels read back yet.",
|
||||||
"blocker": "Current blocker: {value}",
|
"blocker": "Current blocker: {value}",
|
||||||
"boundary": "Metadata-only labels; no raw log read, no secret display, no writeback, and no runtime apply from this panel.",
|
"boundary": "Metadata-only labels; no raw log read, no secret display, no writeback, and no runtime apply from this panel.",
|
||||||
|
"details": "Expand technical fields",
|
||||||
|
"visual": {
|
||||||
|
"blocker": "Current blocker",
|
||||||
|
"diagnosis": "SSH diagnosis",
|
||||||
|
"receipt": "Receipt contract",
|
||||||
|
"receiptValue": "{inputs} inputs / {outputs} outputs"
|
||||||
|
},
|
||||||
|
"rootCause": {
|
||||||
|
"sessionTimeout": "Key accepted, session timeout",
|
||||||
|
"offerTimeout": "Publickey offer timeout",
|
||||||
|
"unknown": "Waiting for queue diagnosis"
|
||||||
|
},
|
||||||
"metrics": {
|
"metrics": {
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"groups": "Groups",
|
"groups": "Groups",
|
||||||
|
|||||||
@@ -9035,13 +9035,25 @@
|
|||||||
"blocked": "阻塞"
|
"blocked": "阻塞"
|
||||||
},
|
},
|
||||||
"aiLoopLogSources": {
|
"aiLoopLogSources": {
|
||||||
"eyebrow": "AI Loop 來源貼標",
|
"eyebrow": "P0 視覺狀態",
|
||||||
"title": "LOG 分群與學習路由",
|
"title": "目前卡點與修復收件",
|
||||||
"subtitle": "從 priority work-order 讀回 current blocker 的 metadata-only tags,讓 KM、RAG、PlayBook、MCP、Verifier 與 AI Agent 用同一組來源維度學習。",
|
"subtitle": "先顯示目前真正卡住的 blocker、SSH 診斷與收件契約;技術欄位收在展開區,避免把工作台變成長文字清單。",
|
||||||
"loading": "讀取 LOG 來源貼標中",
|
"loading": "讀取 LOG 來源貼標中",
|
||||||
"empty": "尚未讀回 LOG 來源貼標。",
|
"empty": "尚未讀回 LOG 來源貼標。",
|
||||||
"blocker": "Current blocker:{value}",
|
"blocker": "Current blocker:{value}",
|
||||||
"boundary": "只顯示 metadata-only 標籤;不讀 raw log、不顯示 secret、不觸發寫入或 runtime apply。",
|
"boundary": "只顯示 metadata-only 標籤;不讀 raw log、不顯示 secret、不觸發寫入或 runtime apply。",
|
||||||
|
"details": "展開技術欄位",
|
||||||
|
"visual": {
|
||||||
|
"blocker": "目前卡點",
|
||||||
|
"diagnosis": "SSH 診斷",
|
||||||
|
"receipt": "收件契約",
|
||||||
|
"receiptValue": "{inputs} inputs / {outputs} outputs"
|
||||||
|
},
|
||||||
|
"rootCause": {
|
||||||
|
"sessionTimeout": "Key accepted,session timeout",
|
||||||
|
"offerTimeout": "Publickey offer timeout",
|
||||||
|
"unknown": "等待 queue 診斷"
|
||||||
|
},
|
||||||
"metrics": {
|
"metrics": {
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"groups": "分群鍵",
|
"groups": "分群鍵",
|
||||||
|
|||||||
@@ -7863,14 +7863,50 @@ function AiLoopLogSourceTagsPanel({
|
|||||||
ids: receiptOutputIds,
|
ids: receiptOutputIds,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
const rootCause = queueNormalizerFieldIds.some((id) =>
|
||||||
|
id.includes("server_accepts_key_then_session_timeout")
|
||||||
|
)
|
||||||
|
? t("rootCause.sessionTimeout")
|
||||||
|
: queueNormalizerFieldIds.some((id) => id.includes("publickey_offer_timeout"))
|
||||||
|
? t("rootCause.offerTimeout")
|
||||||
|
: t("rootCause.unknown");
|
||||||
|
const visualCards = [
|
||||||
|
{
|
||||||
|
key: "blocker",
|
||||||
|
icon: TriangleAlert,
|
||||||
|
label: t("visual.blocker"),
|
||||||
|
value: summary?.ai_loop_current_blocker_id ?? "--",
|
||||||
|
tone: "border-[#f0c6a8] bg-[#fff8f1] text-[#9a4d16]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "diagnosis",
|
||||||
|
icon: SearchCheck,
|
||||||
|
label: t("visual.diagnosis"),
|
||||||
|
value: rootCause,
|
||||||
|
tone: "border-[#c9d8ea] bg-[#eef5ff] text-[#1f5b9b]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "receipt",
|
||||||
|
icon: ClipboardList,
|
||||||
|
label: t("visual.receipt"),
|
||||||
|
value: t("visual.receiptValue", {
|
||||||
|
inputs:
|
||||||
|
summary?.ai_loop_current_blocker_harbor_recovery_receipt_input_count ??
|
||||||
|
receiptInputIds.length,
|
||||||
|
outputs:
|
||||||
|
summary?.ai_loop_current_blocker_harbor_recovery_receipt_output_contract_count ??
|
||||||
|
receiptOutputIds.length,
|
||||||
|
}),
|
||||||
|
tone: "border-[#cbd7bf] bg-[#f4faef] text-[#3d6b24]",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className="border border-[#e0ddd4] bg-white"
|
className="border border-[#e0ddd4] bg-white"
|
||||||
data-testid="ai-loop-log-source-tags-panel"
|
data-testid="ai-loop-log-source-tags-panel"
|
||||||
>
|
>
|
||||||
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[minmax(0,1.1fr)_minmax(0,1.6fr)]">
|
<div className="bg-white p-4">
|
||||||
<div className="bg-white p-4">
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Database className="h-5 w-5 text-[#4A90D9]" aria-hidden="true" />
|
<Database className="h-5 w-5 text-[#4A90D9]" aria-hidden="true" />
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
@@ -7885,6 +7921,30 @@ function AiLoopLogSourceTagsPanel({
|
|||||||
<p className="mt-3 text-xs leading-5 text-[#77736a]">
|
<p className="mt-3 text-xs leading-5 text-[#77736a]">
|
||||||
{t("subtitle")}
|
{t("subtitle")}
|
||||||
</p>
|
</p>
|
||||||
|
<div className="mt-4 grid gap-3 lg:grid-cols-3">
|
||||||
|
{visualCards.map((card) => {
|
||||||
|
const Icon = card.icon;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={card.key}
|
||||||
|
className={cn(
|
||||||
|
"min-w-0 border px-4 py-3",
|
||||||
|
card.tone
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span className="text-xs font-semibold">
|
||||||
|
{card.label}
|
||||||
|
</span>
|
||||||
|
<Icon className="h-4 w-4 shrink-0" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 break-words font-mono text-sm font-semibold leading-5 text-[#141413]">
|
||||||
|
{loading ? "--" : card.value}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
<div className="mt-4 grid gap-px border border-[#e0ddd4] bg-[#e0ddd4] sm:grid-cols-2 xl:grid-cols-4">
|
<div className="mt-4 grid gap-px border border-[#e0ddd4] bg-[#e0ddd4] sm:grid-cols-2 xl:grid-cols-4">
|
||||||
{metrics.map((metric) => {
|
{metrics.map((metric) => {
|
||||||
const Icon = metric.icon;
|
const Icon = metric.icon;
|
||||||
@@ -7903,26 +7963,6 @@ function AiLoopLogSourceTagsPanel({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 border border-[#d8d3c7] bg-[#faf9f3] px-3 py-2 font-mono text-[11px] leading-5 text-[#5f5b52]">
|
|
||||||
{t("blocker", {
|
|
||||||
value: summary?.ai_loop_current_blocker_id ?? "--",
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 border border-[#d8d3c7] bg-white px-3 py-2">
|
|
||||||
<div className="text-[11px] font-semibold text-[#77736a]">
|
|
||||||
{t("queueFields")}
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
||||||
{queueNormalizerFieldIds.slice(0, 7).map((id) => (
|
|
||||||
<span
|
|
||||||
key={id}
|
|
||||||
className="max-w-full break-all border border-[#c9d8ea] bg-[#eef5ff] px-2 py-0.5 font-mono text-[10px] text-[#1f5b9b]"
|
|
||||||
>
|
|
||||||
{id}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 grid gap-2">
|
<div className="mt-3 grid gap-2">
|
||||||
{receiptMetrics.map((metric) => (
|
{receiptMetrics.map((metric) => (
|
||||||
<div
|
<div
|
||||||
@@ -7950,47 +7990,66 @@ function AiLoopLogSourceTagsPanel({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white p-4">
|
<details className="mt-4 border border-[#d8d3c7] bg-[#faf9f3]">
|
||||||
{tags.length > 0 ? (
|
<summary className="cursor-pointer px-3 py-2 text-xs font-semibold text-[#5f5b52]">
|
||||||
<div className="grid gap-2 sm:grid-cols-2 xl:grid-cols-3">
|
{t("details")}
|
||||||
{tags.map((tag) => {
|
</summary>
|
||||||
const key = String(tag.tag_key ?? "");
|
<div className="border-t border-[#d8d3c7] bg-white p-3">
|
||||||
return (
|
<div className="border border-[#d8d3c7] bg-white px-3 py-2">
|
||||||
<div
|
<div className="text-[11px] font-semibold text-[#77736a]">
|
||||||
|
{t("queueFields")}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||||
|
{queueNormalizerFieldIds.map((id) => (
|
||||||
|
<span
|
||||||
|
key={id}
|
||||||
|
className="max-w-full break-all border border-[#c9d8ea] bg-[#eef5ff] px-2 py-0.5 font-mono text-[10px] text-[#1f5b9b]"
|
||||||
|
>
|
||||||
|
{id}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{tags.length > 0 ? (
|
||||||
|
<div className="mt-3 grid gap-2 sm:grid-cols-2 xl:grid-cols-3">
|
||||||
|
{tags.map((tag) => {
|
||||||
|
const key = String(tag.tag_key ?? "");
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={key}
|
||||||
|
className="min-w-0 border border-[#d8d3c7] bg-[#faf9f3] px-3 py-2"
|
||||||
|
>
|
||||||
|
<div className="text-[10px] font-semibold uppercase tracking-[0.08em] text-[#9a968c]">
|
||||||
|
{tagDisplayLabel(key, labelMap)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 break-all font-mono text-[11px] leading-5 text-[#141413]">
|
||||||
|
{tag.tag_value || "--"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-3 border border-[#d8d3c7] bg-[#faf9f3] px-3 py-6 text-center text-xs text-[#77736a]">
|
||||||
|
{loading ? t("loading") : t("empty")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="mt-3 flex flex-wrap gap-1.5">
|
||||||
|
{groupingKeys.map((key) => (
|
||||||
|
<span
|
||||||
key={key}
|
key={key}
|
||||||
className="min-w-0 border border-[#d8d3c7] bg-[#faf9f3] px-3 py-2"
|
className="border border-[#c9d8ea] bg-[#eef5ff] px-2 py-0.5 font-mono text-[11px] text-[#1f5b9b]"
|
||||||
>
|
>
|
||||||
<div className="text-[10px] font-semibold uppercase tracking-[0.08em] text-[#9a968c]">
|
{tagDisplayLabel(key, labelMap)}
|
||||||
{tagDisplayLabel(key, labelMap)}
|
</span>
|
||||||
</div>
|
))}
|
||||||
<div className="mt-1 break-all font-mono text-[11px] leading-5 text-[#141413]">
|
</div>
|
||||||
{tag.tag_value || "--"}
|
<p className="mt-3 text-[11px] leading-5 text-[#77736a]">
|
||||||
</div>
|
{t("boundary")}
|
||||||
</div>
|
</p>
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</details>
|
||||||
<div className="border border-[#d8d3c7] bg-[#faf9f3] px-3 py-6 text-center text-xs text-[#77736a]">
|
|
||||||
{loading ? t("loading") : t("empty")}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="mt-3 flex flex-wrap gap-1.5">
|
|
||||||
{groupingKeys.map((key) => (
|
|
||||||
<span
|
|
||||||
key={key}
|
|
||||||
className="border border-[#c9d8ea] bg-[#eef5ff] px-2 py-0.5 font-mono text-[11px] text-[#1f5b9b]"
|
|
||||||
>
|
|
||||||
{tagDisplayLabel(key, labelMap)}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<p className="mt-3 text-[11px] leading-5 text-[#77736a]">
|
|
||||||
{t("boundary")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user