mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 08:10:47 +08:00
Tighten biz compare fullscreen board for cutover scanning.
Collapse equal pair cells, hide display columns by default, fold field filters, and keep failed sheets first so diffs stand out. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
ee3ed26c88
commit
8a79e962a8
4 changed files with 259 additions and 88 deletions
|
|
@ -511,6 +511,9 @@ const en = {
|
|||
phaseQueued: "Queued",
|
||||
phaseLoading: "Loading",
|
||||
phaseSqlCount: "Counting",
|
||||
phaseSqlProject: "Preparing tables",
|
||||
phaseSqlJoin: "Joining",
|
||||
phaseSqlFailFetch: "Fetching fails",
|
||||
phaseComparing: "Comparing",
|
||||
phasePersisting: "Writing",
|
||||
phasePersistingFail: "Writing fails",
|
||||
|
|
@ -520,6 +523,11 @@ const en = {
|
|||
phaseCancelled: "Cancelled",
|
||||
sheetPending: "Pending",
|
||||
colProgress: "Progress / result",
|
||||
passRateScope: "all rows",
|
||||
keyFiltersToggle: "Field filters",
|
||||
hideDisplayCols: "Hide display cols",
|
||||
showDisplayCols: "Show display cols",
|
||||
hideDisplayColsHint: "Compact: hide display-only columns so keys and diffs stand out",
|
||||
ranWithDuration: "Compare finished ({{s}}s)",
|
||||
unchangedNotStored: "Success rows were counted but not stored. Set “Store success rows” to sample and re-run for spot-check.",
|
||||
unchangedSampleHint: "{{total}} success rows total; browsing a stratified sample of {{listed}}. Search any route for live source lookup. Pass rate uses all {{total}}.",
|
||||
|
|
|
|||
|
|
@ -510,6 +510,9 @@ const zh = {
|
|||
phaseQueued: "排队中",
|
||||
phaseLoading: "加载数据",
|
||||
phaseSqlCount: "库内统计",
|
||||
phaseSqlProject: "准备比对表",
|
||||
phaseSqlJoin: "库内比对",
|
||||
phaseSqlFailFetch: "拉取失败行",
|
||||
phaseComparing: "比对中",
|
||||
phasePersisting: "写入结果",
|
||||
phasePersistingFail: "写入失败行",
|
||||
|
|
@ -519,6 +522,11 @@ const zh = {
|
|||
phaseCancelled: "已取消",
|
||||
sheetPending: "等待中",
|
||||
colProgress: "进度 / 结果",
|
||||
passRateScope: "全表",
|
||||
keyFiltersToggle: "字段筛选",
|
||||
hideDisplayCols: "隐藏展示列",
|
||||
showDisplayCols: "显示展示列",
|
||||
hideDisplayColsHint: "紧凑模式:默认隐藏不参与比对的展示列,突出 Key 与差异",
|
||||
ranWithDuration: "比对完成(耗时 {{s}} 秒)",
|
||||
unchangedNotStored: "成功行仅统计数量未落库。可在任务配置将「成功行保存」改为抽样后重新比对(抽查用)。",
|
||||
unchangedSampleHint: "成功共 {{total}} 条,明细抽样 {{listed}} 条(分层抽查)。要查任意路由请输入筛选条件——将按原表即时判定。通过率按全部 {{total}} 计。",
|
||||
|
|
|
|||
|
|
@ -12970,10 +12970,6 @@ body:has(.bs-cmp-board:fullscreen) {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bs-cmp-val-cell--diff {
|
||||
background: rgba(245, 158, 11, 0.08);
|
||||
}
|
||||
|
||||
.bs-cmp-pair {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -12981,6 +12977,10 @@ body:has(.bs-cmp-board:fullscreen) {
|
|||
min-width: 4.5rem;
|
||||
}
|
||||
|
||||
.bs-cmp-pair--same {
|
||||
min-width: 3rem;
|
||||
}
|
||||
|
||||
.bs-cmp-pair__row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6em minmax(0, 1fr);
|
||||
|
|
@ -13005,6 +13005,20 @@ body:has(.bs-cmp-board:fullscreen) {
|
|||
color: #86efac;
|
||||
}
|
||||
|
||||
.bs-cmp-val-cell--pair.is-same {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.bs-cmp-val--same {
|
||||
color: #cbd5e1;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.bs-cmp-val-cell--diff {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.22);
|
||||
}
|
||||
|
||||
.bs-cmp-val {
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||
color: #e2e8f0;
|
||||
|
|
@ -13012,6 +13026,63 @@ body:has(.bs-cmp-board:fullscreen) {
|
|||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.bs-cmp-progress.is-compact {
|
||||
padding: 6px 10px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.bs-cmp-progress.is-compact .bs-cmp-progress__track {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.bs-cmp-progress.is-compact .bs-cmp-progress__meta {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.bs-cmp-strip__pass-scope {
|
||||
margin-left: 4px;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.bs-cmp-strip__toggle {
|
||||
flex: 0 0 auto;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: rgba(15, 23, 42, 0.45);
|
||||
color: #cbd5e1;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bs-cmp-strip__toggle:hover {
|
||||
border-color: rgba(96, 165, 250, 0.45);
|
||||
color: #f1f5f9;
|
||||
}
|
||||
|
||||
.bs-cmp-strip__toggle.is-active {
|
||||
border-color: rgba(59, 130, 246, 0.5);
|
||||
background: rgba(37, 99, 235, 0.18);
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
.bs-cmp-strip__hint {
|
||||
flex: 1 1 100%;
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.bs-cmp-strip .bs-cmp-key-filters {
|
||||
flex: 1 1 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.bs-cmp-val.is-empty {
|
||||
color: #64748b;
|
||||
font-style: italic;
|
||||
|
|
|
|||
|
|
@ -196,6 +196,8 @@ function PairCell(props: {
|
|||
const post = afterText || "—";
|
||||
const isAdded = kind === "added";
|
||||
const isRemoved = kind === "removed";
|
||||
const same =
|
||||
!mismatch && !isAdded && !isRemoved && String(beforeText || "") === String(afterText || "");
|
||||
// Whole-row missing/extra: emphasize the present side; do not strike it out.
|
||||
const preClass = [
|
||||
"bs-cmp-val",
|
||||
|
|
@ -217,20 +219,31 @@ function PairCell(props: {
|
|||
<td
|
||||
className={`bs-cmp-val-cell bs-cmp-val-cell--pair${
|
||||
mismatch || isAdded || isRemoved ? " bs-cmp-val-cell--diff" : ""
|
||||
}${isAdded ? " is-added" : ""}${isRemoved ? " is-removed" : ""}${
|
||||
}${same ? " is-same" : ""}${isAdded ? " is-added" : ""}${isRemoved ? " is-removed" : ""}${
|
||||
zoneStart ? " bs-cmp-zone-start" : ""
|
||||
}`}
|
||||
title={
|
||||
same
|
||||
? pre
|
||||
: `${beforeLabel} ${isAdded ? "—" : pre} → ${afterLabel} ${isRemoved ? "—" : post}`
|
||||
}
|
||||
>
|
||||
<div className="bs-cmp-pair">
|
||||
<div className="bs-cmp-pair__row">
|
||||
<span className="bs-cmp-pair__tag">{beforeLabel}</span>
|
||||
<span className={preClass}>{isAdded ? "—" : pre}</span>
|
||||
{same ? (
|
||||
<div className="bs-cmp-pair bs-cmp-pair--same">
|
||||
<span className="bs-cmp-val bs-cmp-val--same">{pre || "—"}</span>
|
||||
</div>
|
||||
<div className="bs-cmp-pair__row">
|
||||
<span className="bs-cmp-pair__tag">{afterLabel}</span>
|
||||
<span className={postClass}>{isRemoved ? "—" : post}</span>
|
||||
) : (
|
||||
<div className="bs-cmp-pair">
|
||||
<div className="bs-cmp-pair__row">
|
||||
<span className="bs-cmp-pair__tag">{beforeLabel}</span>
|
||||
<span className={preClass}>{isAdded ? "—" : pre}</span>
|
||||
</div>
|
||||
<div className="bs-cmp-pair__row">
|
||||
<span className="bs-cmp-pair__tag">{afterLabel}</span>
|
||||
<span className={postClass}>{isRemoved ? "—" : post}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{reason ? <div className="bs-cmp-val-reason muted">{reason}</div> : null}
|
||||
</td>
|
||||
);
|
||||
|
|
@ -363,6 +376,9 @@ function localizeRunPhase(phase: string | undefined, t: (key: string) => string)
|
|||
queued: "bizCompare.phaseQueued",
|
||||
loading: "bizCompare.phaseLoading",
|
||||
sql_count: "bizCompare.phaseSqlCount",
|
||||
sql_project: "bizCompare.phaseSqlProject",
|
||||
sql_join: "bizCompare.phaseSqlJoin",
|
||||
sql_fail_fetch: "bizCompare.phaseSqlFailFetch",
|
||||
comparing: "bizCompare.phaseComparing",
|
||||
done: "bizCompare.phaseDone",
|
||||
failed: "bizCompare.phaseFailed",
|
||||
|
|
@ -944,6 +960,8 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
const [resultKw, setResultKw] = useState("");
|
||||
const debouncedResultKw = useDebouncedValue(resultKw, 300);
|
||||
const [resultKeyFilters, setResultKeyFilters] = useState<Record<string, string>>({});
|
||||
const [keyFiltersOpen, setKeyFiltersOpen] = useState(false);
|
||||
const [hideDisplayCols, setHideDisplayCols] = useState(true);
|
||||
const debouncedKeyFiltersJson = useDebouncedValue(JSON.stringify(resultKeyFilters), 300);
|
||||
const debouncedKeyFilters = useMemo(() => {
|
||||
try {
|
||||
|
|
@ -959,6 +977,10 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
}, [debouncedKeyFiltersJson]);
|
||||
const hasResultSearch =
|
||||
Boolean(debouncedResultKw.trim()) || Object.keys(debouncedKeyFilters).length > 0;
|
||||
const activeKeyFilterCount = Object.keys(resultKeyFilters).filter((k) =>
|
||||
String(resultKeyFilters[k] || "").trim(),
|
||||
).length;
|
||||
const keyFiltersVisible = keyFiltersOpen || activeKeyFilterCount > 0;
|
||||
const [resultPage, setResultPage] = useState(1);
|
||||
const [resultPageSize, setResultPageSize] = useState(100);
|
||||
const [resultTotal, setResultTotal] = useState(0);
|
||||
|
|
@ -1419,7 +1441,12 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
1 +
|
||||
(showFailCol ? 1 : 0) +
|
||||
resultColumns.keys.length +
|
||||
Math.max(resultColumns.extras.length, 0);
|
||||
Math.max(
|
||||
resultColumns.extras.filter(
|
||||
(f) => resultColumns.compareSet.has(f) || !hideDisplayCols,
|
||||
).length,
|
||||
0,
|
||||
);
|
||||
|
||||
const rememberTableScroll = useCallback(() => {
|
||||
const wrap = tableScrollRef.current;
|
||||
|
|
@ -3404,7 +3431,7 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
</div>
|
||||
|
||||
{runDetail && runIsActive ? (
|
||||
<div className="bs-cmp-progress" role="status" aria-live="polite">
|
||||
<div className="bs-cmp-progress is-compact" role="status" aria-live="polite">
|
||||
<div className="bs-cmp-progress__head">
|
||||
<NmStatusChip color="accent">{t("bizCompare.runStatusRunning")}</NmStatusChip>
|
||||
{runEngine === "sql" || runEngine === "python" ? (
|
||||
|
|
@ -3537,36 +3564,54 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
<div className="bs-cmp-nav__list" role="tablist">
|
||||
{(sheetCards.length
|
||||
? sheetCards
|
||||
: runSheets.map((s) => {
|
||||
const removed = Number(s.summary?.removed || 0);
|
||||
const changed = Number(s.summary?.changed || 0);
|
||||
const unchanged = Number(s.summary?.unchanged || 0);
|
||||
const fail = removed + changed;
|
||||
const judged = fail + unchanged;
|
||||
const st = String((s as any).status || "done");
|
||||
const pending = ["pending", "running", "queued"].includes(st);
|
||||
return {
|
||||
sheet_id: sheetIdentity(s),
|
||||
title: sheetLabel(s),
|
||||
metric_id: s.metric_id,
|
||||
mode: s.mode,
|
||||
status: st,
|
||||
added: s.summary?.added,
|
||||
removed,
|
||||
changed,
|
||||
unchanged,
|
||||
before_count: s.summary?.before_count,
|
||||
after_count: s.summary?.after_count,
|
||||
fail_count: fail,
|
||||
success_count: unchanged,
|
||||
diff_count: fail,
|
||||
pass_rate: pending
|
||||
? null
|
||||
: judged
|
||||
? Math.round((unchanged / judged) * 1000) / 10
|
||||
: 100,
|
||||
};
|
||||
})
|
||||
: [...runSheets]
|
||||
.map((s) => {
|
||||
const removed = Number(s.summary?.removed || 0);
|
||||
const changed = Number(s.summary?.changed || 0);
|
||||
const unchanged = Number(s.summary?.unchanged || 0);
|
||||
const fail = removed + changed;
|
||||
const judged = fail + unchanged;
|
||||
const st = String((s as any).status || "done");
|
||||
const pending = ["pending", "running", "queued"].includes(st);
|
||||
return {
|
||||
sheet_id: sheetIdentity(s),
|
||||
title: sheetLabel(s),
|
||||
metric_id: s.metric_id,
|
||||
mode: s.mode,
|
||||
status: st,
|
||||
added: s.summary?.added,
|
||||
removed,
|
||||
changed,
|
||||
unchanged,
|
||||
before_count: s.summary?.before_count,
|
||||
after_count: s.summary?.after_count,
|
||||
fail_count: fail,
|
||||
success_count: unchanged,
|
||||
diff_count: fail,
|
||||
pass_rate: pending
|
||||
? null
|
||||
: judged
|
||||
? Math.round((unchanged / judged) * 1000) / 10
|
||||
: 100,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const pa = ["pending", "running", "queued"].includes(
|
||||
String(a.status || ""),
|
||||
)
|
||||
? 1
|
||||
: 0;
|
||||
const pb = ["pending", "running", "queued"].includes(
|
||||
String(b.status || ""),
|
||||
)
|
||||
? 1
|
||||
: 0;
|
||||
if (pa !== pb) return pa - pb;
|
||||
const da = Number(a.fail_count || 0);
|
||||
const db = Number(b.fail_count || 0);
|
||||
if (da !== db) return db - da;
|
||||
return String(a.metric_id).localeCompare(String(b.metric_id));
|
||||
})
|
||||
).map((c) => {
|
||||
const fail = sheetFailOf(c);
|
||||
const ok = sheetSuccessOf(c);
|
||||
|
|
@ -3586,11 +3631,17 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
className={`bs-cmp-nav__item${active ? " is-active" : ""}${
|
||||
pending ? " is-pending" : fail > 0 ? " has-diff" : " is-clean"
|
||||
}`}
|
||||
onClick={() => setResultSheetId(id)}
|
||||
onClick={() => {
|
||||
setResultSheetId(id);
|
||||
// Cutover default: land on fails when switching sheets
|
||||
if (fail > 0 && kindFilter === "unchanged") {
|
||||
setKindFilter("diff");
|
||||
}
|
||||
}}
|
||||
title={
|
||||
pending
|
||||
? `${label} · ${t("bizCompare.sheetPending")}`
|
||||
: `${label} · ${t("bizCompare.kindFail")} ${fail} · ${t("bizCompare.kindSuccess")} ${ok} · ${t("bizCompare.passRateShort")} ${rate}%`
|
||||
: `${label} · ${t("bizCompare.kindFail")} ${fail} · ${t("bizCompare.kindSuccess")} ${ok} · ${t("bizCompare.passRateShort")} ${rate ?? "…"}%`
|
||||
}
|
||||
>
|
||||
<span className="bs-cmp-nav__dot" aria-hidden />
|
||||
|
|
@ -3650,9 +3701,15 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
: ` · ${activeSheetCard?.before_count ?? 0}→${activeSheetCard?.after_count ?? 0}`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-cmp-strip__pass" title={t("bizCompare.passRate")}>
|
||||
<div
|
||||
className="bs-cmp-strip__pass"
|
||||
title={`${t("bizCompare.passRate")} · ${t("bizCompare.passRateScope")}`}
|
||||
>
|
||||
<span className="bs-cmp-strip__pass-label">
|
||||
{t("bizCompare.passRate")}
|
||||
<span className="bs-cmp-strip__pass-scope">
|
||||
{t("bizCompare.passRateScope")}
|
||||
</span>
|
||||
</span>
|
||||
<span className="bs-cmp-strip__pass-value">
|
||||
{activeSheetPending || activePassRate === null
|
||||
|
|
@ -3708,9 +3765,31 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
<span className="muted bs-sheet-count">
|
||||
{diffsLoading ? "…" : `${pagedDiffs.length}/${resultTotal}`}
|
||||
</span>
|
||||
{resultSearchKeyFields.length ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`bs-cmp-strip__toggle${keyFiltersVisible ? " is-active" : ""}`}
|
||||
onClick={() => setKeyFiltersOpen((v) => !v)}
|
||||
>
|
||||
{t("bizCompare.keyFiltersToggle")}
|
||||
{activeKeyFilterCount
|
||||
? ` (${activeKeyFilterCount})`
|
||||
: ""}
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className={`bs-cmp-strip__toggle${hideDisplayCols ? " is-active" : ""}`}
|
||||
title={t("bizCompare.hideDisplayColsHint")}
|
||||
onClick={() => setHideDisplayCols((v) => !v)}
|
||||
>
|
||||
{hideDisplayCols
|
||||
? t("bizCompare.showDisplayCols")
|
||||
: t("bizCompare.hideDisplayCols")}
|
||||
</button>
|
||||
</div>
|
||||
{resultSearchKeyFields.length ? (
|
||||
<div className="bs-cmp-key-filters" style={{ marginTop: 6 }}>
|
||||
{resultSearchKeyFields.length && keyFiltersVisible ? (
|
||||
<div className="bs-cmp-key-filters">
|
||||
{resultSearchKeyFields.map((f) => (
|
||||
<label key={f} className="bs-cmp-key-filter">
|
||||
<span className="muted">{f}</span>
|
||||
|
|
@ -3735,9 +3814,7 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
/>
|
||||
</label>
|
||||
))}
|
||||
{Object.keys(resultKeyFilters).some((k) =>
|
||||
String(resultKeyFilters[k] || "").trim(),
|
||||
) ? (
|
||||
{activeKeyFilterCount ? (
|
||||
<button
|
||||
type="button"
|
||||
className="linkish"
|
||||
|
|
@ -3749,7 +3826,7 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
</div>
|
||||
) : null}
|
||||
{isLiveSearch ? (
|
||||
<p className="muted bm-hint" style={{ margin: "4px 0 0" }}>
|
||||
<p className="muted bm-hint bs-cmp-strip__hint">
|
||||
{diffsTruncated
|
||||
? t("bizCompare.liveSearchTruncatedHint")
|
||||
: t("bizCompare.liveSearchHint")}
|
||||
|
|
@ -3773,7 +3850,7 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
runDetail?.summary?.unchanged_listed ??
|
||||
0,
|
||||
) > 0)) ? (
|
||||
<p className="muted bm-hint" style={{ margin: "4px 0 0" }}>
|
||||
<p className="muted bm-hint bs-cmp-strip__hint">
|
||||
{t("bizCompare.unchangedSampleHint", {
|
||||
listed: String(
|
||||
Number(
|
||||
|
|
@ -3808,9 +3885,11 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
const compareCols = resultColumns.extras.filter((f) =>
|
||||
resultColumns.compareSet.has(f),
|
||||
);
|
||||
const displayCols = resultColumns.extras.filter(
|
||||
(f) => !resultColumns.compareSet.has(f),
|
||||
);
|
||||
const displayCols = hideDisplayCols
|
||||
? []
|
||||
: resultColumns.extras.filter(
|
||||
(f) => !resultColumns.compareSet.has(f),
|
||||
);
|
||||
const verdictColSpan = showFailCol ? 2 : 1;
|
||||
const hasGroups =
|
||||
keyCols.length + compareCols.length + displayCols.length > 0;
|
||||
|
|
@ -3942,36 +4021,41 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage
|
|||
{cellText(d.key?.[k] ?? pre[k] ?? post[k]) || "—"}
|
||||
</td>
|
||||
))}
|
||||
{resultColumns.extras.map((f, fi) => {
|
||||
const pv = cellText(pre[f]);
|
||||
const av = cellText(post[f]);
|
||||
const ch = d.changes?.[f];
|
||||
const isCmp = resultColumns.compareSet.has(f);
|
||||
const prev = resultColumns.extras[fi - 1];
|
||||
const prevCmp = prev
|
||||
? resultColumns.compareSet.has(prev)
|
||||
: null;
|
||||
const zoneStart = fi === 0 || prevCmp !== isCmp;
|
||||
const mismatch =
|
||||
d.kind === "added" || d.kind === "removed"
|
||||
? Boolean(pv || av)
|
||||
: isCmp
|
||||
? Boolean(ch)
|
||||
: pv !== av;
|
||||
return (
|
||||
<PairCell
|
||||
key={f}
|
||||
beforeText={pv}
|
||||
afterText={av}
|
||||
kind={d.kind}
|
||||
mismatch={mismatch}
|
||||
reason={ch?.reason}
|
||||
beforeLabel={t("bizCompare.pairBefore")}
|
||||
afterLabel={t("bizCompare.pairAfter")}
|
||||
zoneStart={zoneStart}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{resultColumns.extras
|
||||
.filter(
|
||||
(f) =>
|
||||
resultColumns.compareSet.has(f) || !hideDisplayCols,
|
||||
)
|
||||
.map((f, fi, visibleExtras) => {
|
||||
const pv = cellText(pre[f]);
|
||||
const av = cellText(post[f]);
|
||||
const ch = d.changes?.[f];
|
||||
const isCmp = resultColumns.compareSet.has(f);
|
||||
const prev = visibleExtras[fi - 1];
|
||||
const prevCmp = prev
|
||||
? resultColumns.compareSet.has(prev)
|
||||
: null;
|
||||
const zoneStart = fi === 0 || prevCmp !== isCmp;
|
||||
const mismatch =
|
||||
d.kind === "added" || d.kind === "removed"
|
||||
? Boolean(pv || av)
|
||||
: isCmp
|
||||
? Boolean(ch)
|
||||
: pv !== av;
|
||||
return (
|
||||
<PairCell
|
||||
key={f}
|
||||
beforeText={pv}
|
||||
afterText={av}
|
||||
kind={d.kind}
|
||||
mismatch={mismatch}
|
||||
reason={ch?.reason}
|
||||
beforeLabel={t("bizCompare.pairBefore")}
|
||||
afterLabel={t("bizCompare.pairAfter")}
|
||||
zoneStart={zoneStart}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue