Show full copyable batch ids in biz-state/cutover UI and document batch list tools.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-20 15:26:46 +08:00
parent 04850b942c
commit 83cf086d8c
6 changed files with 123 additions and 22 deletions

View file

@ -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 }[]) => (
<>
<option value="">—</option>
{items.map((x) => (
<option key={x.id} value={x.id}>
{fmtTime(x.started_at) !== "—" ? fmtTime(x.started_at) : x.id.slice(0, 12)}
</option>
))}
{items.map((x) => {
const when = fmtTime(x.started_at);
const label = when !== "—" ? `${when} · ${x.id}` : x.id;
return (
<option key={x.id} value={x.id} title={x.id}>
{label}
</option>
);
})}
</>
);
const copyBatchId = (id: string) => {
void (async () => {
const ok = await writeClipboardText(id);
if (ok) showOk(t("common.copied"));
else showError(t("common.opFailed"));
})();
};
return (
<section className="panel nm-page-panel">
<div className="panel__toolbar">
@ -2340,8 +2353,8 @@ export function BizMigrationPage() {
aria-label={t("bizMigration.batches")}
>
{batches.map((b) => (
<option key={b.id} value={b.id}>
{b.batch_label} ({statusLabel(b.status)})
<option key={b.id} value={b.id} title={b.id}>
{b.batch_label} ({statusLabel(b.status)}) · {b.id}
</option>
))}
</FieldSelect>
@ -2397,6 +2410,15 @@ export function BizMigrationPage() {
) : null}
</div>
</div>
{batch ? (
<div className="bs-id-row" title={batch.id}>
<span className="bs-id-row__label">{t("bizMigration.batchId")}</span>
<code className="bs-id-row__value">{batch.id}</code>
<Button size="sm" variant="ghost" onPress={() => copyBatchId(batch.id)}>
{t("bizMigration.copyBatchId")}
</Button>
</div>
) : null}
<div className="ct-editor__main bm-batches__main">
<aside className="ct-editor__nav" aria-label={t("bizMigration.pickExpectPorts")}>
@ -2530,11 +2552,20 @@ export function BizMigrationPage() {
aria-label={t("bizMigration.batches")}
>
{batches.map((b) => (
<option key={b.id} value={b.id}>
{b.batch_label} ({statusLabel(b.status)})
<option key={b.id} value={b.id} title={b.id}>
{b.batch_label} ({statusLabel(b.status)}) · {b.id}
</option>
))}
</FieldSelect>
{batchId ? (
<div className="bs-id-row bs-id-row--inline" title={batchId}>
<span className="bs-id-row__label">{t("bizMigration.batchId")}</span>
<code className="bs-id-row__value">{batchId}</code>
<Button size="sm" variant="ghost" onPress={() => copyBatchId(batchId)}>
{t("bizMigration.copyBatchId")}
</Button>
</div>
) : null}
<FieldSelect
value={pinOldBatchId}
onChange={(e) => setPinOldBatchId(e.target.value)}

View file

@ -30,6 +30,7 @@ import {
} from "../../services/api";
import type { CliTargetItem } from "../../types";
import { pageCount } from "../../utils/display";
import { writeClipboardText } from "../../utils/clipboard";
import { formatSystemTime } from "../../utils/time";
import { jobChipColor, NmStatusChip, sourceChipColor } from "./nmChips";
@ -1502,9 +1503,7 @@ export function BizStatePage() {
className="app-heroui-modal--xl bs-workbook-modal"
>
<Modal.Header>
<Modal.Heading>
{t("bizState.batchWorkbook")} · {batchDetail?.id?.slice(0, 8)}…
</Modal.Heading>
<Modal.Heading>{t("bizState.batchWorkbook")}</Modal.Heading>
<Modal.CloseTrigger />
</Modal.Header>
<Modal.Body className="flex flex-col gap-2 bs-workbook-body">
@ -1517,6 +1516,24 @@ export function BizStatePage() {
{batchDetail.command_count} cmd · {batchDetail.row_count} rows ·{" "}
{fmtTime(batchDetail.started_at)}
</span>
<div className="bs-id-row" title={String(batchDetail.id || "")}>
<span className="bs-id-row__label">{t("bizState.batchId")}</span>
<code className="bs-id-row__value">{String(batchDetail.id || "")}</code>
<Button
size="sm"
variant="ghost"
isDisabled={!batchDetail.id}
onPress={() => {
void (async () => {
const ok = await writeClipboardText(String(batchDetail.id || ""));
if (ok) showOk(t("common.copied"));
else showError(t("common.opFailed"));
})();
}}
>
{t("bizState.copyBatchId")}
</Button>
</div>
</div>
) : null}