From 83cf086d8c4c35243d9e40b584d23e0587111289 Mon Sep 17 00:00:00 2001 From: oliver Date: Sun, 20 Sep 2026 15:26:46 +0800 Subject: [PATCH] Show full copyable batch ids in biz-state/cutover UI and document batch list tools. Co-authored-by: Cursor --- skills/biz-monitor/netx-biz-monitor/SKILL.md | 22 +++++---- web/src/i18n/en.ts | 6 +++ web/src/i18n/zh.ts | 6 +++ web/src/index.css | 39 ++++++++++++++++ web/src/pages/network/BizMigrationPage.tsx | 49 ++++++++++++++++---- web/src/pages/network/BizStatePage.tsx | 23 +++++++-- 6 files changed, 123 insertions(+), 22 deletions(-) diff --git a/skills/biz-monitor/netx-biz-monitor/SKILL.md b/skills/biz-monitor/netx-biz-monitor/SKILL.md index e3fa593..ebd44a9 100644 --- a/skills/biz-monitor/netx-biz-monitor/SKILL.md +++ b/skills/biz-monitor/netx-biz-monitor/SKILL.md @@ -2,10 +2,10 @@ name: netx-biz-monitor description: >- Overnight cutover / biz_state monitor analysis via netxops host tools - (netx__listBizMonitors / netx__getBizMonitor*). List projects/tasks first - when no id is given; then read definitions, board, reds/diffs with evidence - (device-raw A/B, show commands), and judge tool false-positive vs real - business fault. Does not create templates/tasks. + (netx__listBizMonitors / listBizMonitorBatches / getBizMonitor*). List + projects/tasks then batches when no id is given; read definitions, board, + reds/diffs with evidence (device-raw A/B, show commands), and judge tool + false-positive vs real business fault. Does not create templates/tasks. --- # netx-biz-monitor(割接 / 业务监控只读分析) @@ -18,6 +18,7 @@ description: >- | 工具 | 用途 | |------|------| | `netx__listBizMonitors` | **入口目录**:列出割接/监控对比项目 + 业务监控任务(**无需** project_id) | +| `netx__listBizMonitorBatches` | **批次目录**:`project_id`→割接批次;`task_id`→采集批次;`kind=runs`→evaluate run | | `netx__getBizMonitorContext` | 任务定义:项目、监控/对比模板、归一化规则、端口映射、关联任务与命令项 | | `netx__getBizMonitorBoard` | 批次进度、evaluate run、sheet 卡、verdict 统计 | | `netx__listBizMonitorReds` | 红单 + **evidence** | @@ -37,12 +38,13 @@ description: >- ## 推荐顺序 1. 无 id → `listBizMonitors`(`kind=projects|tasks|all`,可加 `q` / `purpose`) -2. `getBizMonitorContext(project_id)` — 弄清盯什么 -3. `getBizMonitorBoard(batch_id)` — 进度 / missing / skipped -4. `listBizMonitorReds` 或 `getBizMonitorDiffs(color=red)` -5. 看 evidence:parse 失败?current_missing?映射 miss?归一化误伤? -6. 需要原文 → `getBizCollectCommandRaw` -7. 像真障 → `execManagedNe` 现场核对 +2. `listBizMonitorBatches(project_id)` 或 `(task_id)` — 拿到 **完整 batch_id**;需要 run 时再 `kind=runs` / `include_runs` +3. `getBizMonitorContext(project_id)` — 弄清盯什么 +4. `getBizMonitorBoard(batch_id)` — 进度 / missing / skipped +5. `listBizMonitorReds` 或 `getBizMonitorDiffs(color=red)` +6. 看 evidence:parse 失败?current_missing?映射 miss?归一化误伤? +7. 需要原文 → `getBizCollectCommandRaw` +8. 像真障 → `execManagedNe` 现场核对 ## Verdict 速查(skill 解释,工具不硬编码) diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index b967fed..44c01e5 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -19,6 +19,8 @@ const en = { opFailed: "Operation failed", empty: "-", cancel: "Cancel", + copy: "Copy", + copied: "Copied", }, workbench: { title: "Workbench", @@ -240,6 +242,8 @@ const en = { noBatches: "No batches yet", batchDetail: "Batch detail", batchWorkbook: "Batch workbook", + batchId: "Batch ID", + copyBatchId: "Copy batch ID", sheetCommands: "Commands", sheetLldp: "LLDP neighbors", sheetVrfRoute: "VRF route summary", @@ -593,6 +597,8 @@ const en = { portFilterPh: "Filter", emptyBaselinePorts: "No baseline data", batchLabel: "Batch label", + batchId: "Batch ID", + copyBatchId: "Copy batch ID", defaultBatchLabel: "Night N", expectPorts: "Expected ports (old side)", expectPortsPh: "Comma or newline separated", diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 5e76664..589fad7 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -19,6 +19,8 @@ const zh = { opFailed: "操作失败", empty: "-", cancel: "取消", + copy: "复制", + copied: "已复制", }, workbench: { title: "工作台", @@ -240,6 +242,8 @@ const zh = { noBatches: "尚无批次", batchDetail: "批次详情", batchWorkbook: "批次工作簿", + batchId: "批次 ID", + copyBatchId: "复制批次 ID", sheetCommands: "命令", sheetLldp: "LLDP 邻居", sheetVrfRoute: "VRF 路由摘要", @@ -586,6 +590,8 @@ const zh = { portFilterPh: "筛选", emptyBaselinePorts: "基线无数据", batchLabel: "批次标签", + batchId: "批次 ID", + copyBatchId: "复制批次 ID", defaultBatchLabel: "第N晚", expectPorts: "本批预期端口(老侧)", expectPortsPh: "逗号或换行分隔", diff --git a/web/src/index.css b/web/src/index.css index 7e76daa..3deef06 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -9953,6 +9953,45 @@ html.login-page--paused .login-page__flare { gap: 8px 12px; } +.bs-id-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + width: 100%; + min-width: 0; + padding: 6px 8px; + border-radius: 6px; + border: 1px solid rgba(148, 163, 184, 0.2); + background: rgba(2, 6, 23, 0.45); +} + +.bs-id-row--inline { + width: auto; + flex: 1 1 220px; +} + +.bs-id-row__label { + flex: 0 0 auto; + font-size: 11px; + letter-spacing: 0.04em; + text-transform: uppercase; + color: #94a3b8; +} + +.bs-id-row__value { + flex: 1 1 auto; + min-width: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 12px; + line-height: 1.4; + color: #e2e8f0; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + word-break: normal; +} + .bs-sheet-cmd-bar { display: flex; flex-direction: column; diff --git a/web/src/pages/network/BizMigrationPage.tsx b/web/src/pages/network/BizMigrationPage.tsx index c0c9e4e..7e1360b 100644 --- a/web/src/pages/network/BizMigrationPage.tsx +++ b/web/src/pages/network/BizMigrationPage.tsx @@ -37,6 +37,7 @@ import { } from "../../services/api"; // bizStateListTasks not needed for create — NEs + auto HF import type { CliTargetItem } from "../../types"; +import { writeClipboardText } from "../../utils/clipboard"; import { pageCount } from "../../utils/display"; import { formatSystemTime, localDatetimeInputToUtcIso, utcIsoToLocalDatetimeInput } from "../../utils/time"; import { jobChipColor, NmStatusChip, sourceChipColor } from "./nmChips"; @@ -1426,14 +1427,26 @@ export function BizMigrationPage() { const batchOptions = (items: { id: string; started_at?: string | null }[]) => ( <> - {items.map((x) => ( - - ))} + {items.map((x) => { + const when = fmtTime(x.started_at); + const label = when !== "—" ? `${when} · ${x.id}` : x.id; + return ( + + ); + })} ); + const copyBatchId = (id: string) => { + void (async () => { + const ok = await writeClipboardText(id); + if (ok) showOk(t("common.copied")); + else showError(t("common.opFailed")); + })(); + }; + return (
@@ -2340,8 +2353,8 @@ export function BizMigrationPage() { aria-label={t("bizMigration.batches")} > {batches.map((b) => ( - ))} @@ -2397,6 +2410,15 @@ export function BizMigrationPage() { ) : null}
+ {batch ? ( +
+ {t("bizMigration.batchId")} + {batch.id} + +
+ ) : null}
) : null}